diff --git a/VERSION b/VERSION index dc39e58..7c483e8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6 \ No newline at end of file +1.7 \ No newline at end of file diff --git a/examples/2d/base/fracture.py b/examples/2d/base/fracture.py index 74d18b9..d4f075e 100644 --- a/examples/2d/base/fracture.py +++ b/examples/2d/base/fracture.py @@ -1,47 +1,46 @@ import numpy as np import ufl import fenics as fen from rrompy.hfengines.linear_problem.bidimensional import \ MembraneFractureEngine as MFE from rrompy.solver.fenics import affine_warping verb = 100 -mu0 = [45. ** .5, .6] +mu0 = [45. ** .5, .7] H = 1. L = .75 delta = .05 n = 50 solver = MFE(mu0 = mu0, H = H, L = L, delta = delta, n = n, verbosity = verb) u0 = solver.liftDirichletData(mu0) uh = solver.solve(mu0)[0] #solver.plotmesh(figsize = (7.5, 4.5)) #solver.plot(u0, what = 'REAL', figsize = (8, 5)) print(solver.norm(uh)) #solver.plot(uh, what = 'REAL', figsize = (8, 5)) #solver.plot(solver.residual(uh, mu0)[0], name = 'res', # what = 'REAL', figsize = (8, 5)) #solver.outParaviewTimeDomain(uh, mu0[0], filename = 'out', folder = True, # forceNewFile = False) -L = mu0[1] y = fen.SpatialCoordinate(solver.V.mesh())[1] warp1, warpI1 = affine_warping(solver.V.mesh(), - np.array([[1, 0], [0, 2. * L]])) + np.array([[1, 0], [0, 2. * mu0[1]]])) warp2, warpI2 = affine_warping(solver.V.mesh(), - np.array([[1, 0], [0, 2. - 2. * L]])) + np.array([[1, 0], [0, 2. - 2. * mu0[1]]])) warp = ufl.conditional(ufl.ge(y, 0.), warp1, warp2) warpI = ufl.conditional(ufl.ge(y, 0.), warpI1, warpI2) #solver.plotmesh([warp, warpI], figsize = (7.5, 4.5)) #solver.plot(u0, [warp, warpI], what = 'REAL', figsize = (8, 5)) solver.plot(uh, [warp, warpI], what = 'REAL', figsize = (8, 5)) #solver.plot(solver.residual(uh, mu0)[0], [warp, warpI], name = 'res', # what = 'REAL', figsize = (8, 5)) #solver.outParaviewTimeDomain(uh, mu0[0], [warp, warpI], # filename = 'outW', folder = True, # forceNewFile = False) diff --git a/examples/2d/base/matrix_passive.py b/examples/2d/base/matrix_passive.py new file mode 100644 index 0000000..cc1331a --- /dev/null +++ b/examples/2d/base/matrix_passive.py @@ -0,0 +1,13 @@ +from rrompy.hfengines.linear_problem.tridimensional import \ + MatrixDynamicalPassive as MDP + +verb = 100 +n = 100 +b = 10 + +mu0 = [0., 10] +mutar = [4., 15] + +solver = MDP(mu0 = mu0, n = n, b = b, verbosity = verb) +uh = solver.solve(mutar)[0] +solver.plot(uh) diff --git a/examples/2d/pod/cookie_single_pod.py b/examples/2d/pod/cookie_single_pod.py index d6d190b..0efdfd4 100644 --- a/examples/2d/pod/cookie_single_pod.py +++ b/examples/2d/pod/cookie_single_pod.py @@ -1,153 +1,134 @@ import numpy as np from rrompy.hfengines.linear_problem.bidimensional import \ CookieEngineSingle as CES -from rrompy.reduction_methods.centered import RationalPade as RP -from rrompy.reduction_methods.distributed import RationalInterpolant as RI -from rrompy.reduction_methods.centered import RBCentered as RBC -from rrompy.reduction_methods.distributed import RBDistributed as RBD +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) verb = 5 size = 1 show_sample = True show_norm = True clip = -1 #clip = .4 #clip = .6 Delta = -10 MN = 15 R = (MN + 2) * (MN + 1) // 2 S = [int(np.ceil(R ** .5))] * 2 PODTol = 1e-6 samples = "centered" -samples = "centered_fake" -samples = "distributed" +samples = "standard" algo = "rational" #algo = "RB" sampling = "quadrature" sampling = "quadrature_total" sampling = "random" -if samples == "distributed": +if samples == "standard": radial = 0 # radial = "gaussian" # radial = "thinplate" # radial = "multiquadric" rW0 = 1. radialWeight = [rW0] * 2 assert Delta <= 0 if size == 1: # below mu0 = [20 ** .5, 1. ** .5] mutar = [20.5 ** .5, 1.05 ** .5] murange = [[18.5 ** .5, .85 ** .5], [21.5 ** .5, 1.15 ** .5]] aEff = 1.#25 bEff = 1. - aEff murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5, aEff*murange[0][1] + bEff*murange[1][1]], [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5, aEff*murange[1][1] + bEff*murange[0][1]]] kappa = 20. ** .5 theta = - np.pi / 6. n = 30 Rad = 1. L = np.pi nX = 2 nY = 1 solver = CES(kappa = kappa, theta = theta, n = n, R = Rad, L = L, nX = nX, nY = nY, mu0 = mu0, verbosity = verb) -rescaling = [lambda x: np.power(x, 2.)] * 2 -rescalingInv = [lambda x: np.power(x, .5)] * 2 +rescalingExp = [2.] * 2 if algo == "rational": params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True} - if samples == "distributed": + if samples == "standard": params['polybasis'] = "CHEBYSHEV" # params['polybasis'] = "LEGENDRE" # params['polybasis'] = "MONOMIAL" params['E'] = MN params['radialBasis'] = radial params['radialBasisWeights'] = radialWeight - method = RI - elif samples == "centered_fake": + elif samples == "centered": params['polybasis'] = "MONOMIAL" params['S'] = R - method = RI - else: - params['S'] = R - method = RP + method = RI else: #if algo == "RB": params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':S, 'POD':True, 'PODTolerance':PODTol} - if samples == "distributed": - method = RBD - elif samples == "centered_fake": - params['S'] = R - method = RBD - else: + if samples == "centered": params['S'] = R - method = RBC + method = RB -if samples == "distributed": +if samples == "standard": if sampling == "quadrature": - params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling, -# scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling, -# scalingInv = rescalingInv) + params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) params['S'] = [max(j, MN + 1) for j in params['S']] elif sampling == "quadrature_total": - params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) params['S'] = R else: # if sampling == "random": - params['sampler'] = RS(murange, "HALTON", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) params['S'] = R -elif samples == "centered_fake": - params['sampler'] = MS(murange, points = [mu0], scaling = rescaling, - scalingInv = rescalingInv) +elif samples == "centered": + params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) -if samples == "distributed": approx.samplingEngine.allowRepeatedSamples = False +if samples == "standard": approx.samplingEngine.allowRepeatedSamples = False approx.setupApprox() if show_sample: L = mutar[1] approx.plotApprox(mutar, name = 'u_app', homogeneized = False, what = "REAL") approx.plotHF(mutar, name = 'u_HF', homogeneized = False, what = "REAL") approx.plotErr(mutar, name = 'err', homogeneized = False, what = "REAL") # approx.plotRes(mutar, name = 'res', homogeneized = False, what = "REAL") appErr = approx.normErr(mutar) solNorm = approx.normHF(mutar) resNorm = approx.normRes(mutar) RHSNorm = approx.normRHS(mutar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) if algo == "rational" and approx.N > 0: from plot_zero_set import plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 2.], clip = clip) if show_norm: solver._solveBatchSize = 100 from plot_inf_set import plotInfSet2 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( murange, murangeEff, approx, mu0, 25, [2., 2.], clip = clip, relative = False) diff --git a/examples/2d/pod/fracture_pod.py b/examples/2d/pod/fracture_pod.py index 1aff9fa..646274d 100644 --- a/examples/2d/pod/fracture_pod.py +++ b/examples/2d/pod/fracture_pod.py @@ -1,194 +1,280 @@ import numpy as np from rrompy.hfengines.linear_problem.bidimensional import \ MembraneFractureEngine as MFE -from rrompy.reduction_methods.centered import RationalPade as RP -from rrompy.reduction_methods.distributed import RationalInterpolant as RI -from rrompy.reduction_methods.centered import RBCentered as RBC -from rrompy.reduction_methods.distributed import RBDistributed as RBD +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB +from rrompy.reduction_methods.pivoted import RationalInterpolantPivoted as RIP +from rrompy.reduction_methods.pivoted import ReducedBasisPivoted as RBP +from rrompy.reduction_methods.pole_matching import \ + RationalInterpolantPoleMatching as RIPM +from rrompy.reduction_methods.pole_matching import \ + ReducedBasisPoleMatching as RBPM from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) -verb = 5 -size = 4 +verb = 70 +size = 2 show_sample = False show_norm = True -clip = -1 -#clip = .4 -#clip = .6 -homogeneize = False -#homogeneize = True Delta = 0 -MN = 20 +MN = 8 R = (MN + 2) * (MN + 1) // 2 S = [int(np.ceil(R ** .5))] * 2 -PODTol = 1e-8 +PODTol = 1e-4 +SPivot = [MN + 1, 4] +MMarginal = SPivot[1] - 1 +matchingWeight = 1. +cutOffTolerance = 1. * np.inf +cutOffType = "MAGNITUDE" samples = "centered" -samples = "centered_fake" -samples = "distributed" +samples = "standard" +samples = "pivoted" +samples = "pole matching" algo = "rational" -#algo = "RB" +algo = "RB" sampling = "quadrature" sampling = "quadrature_total" #sampling = "random" +samplingM = "quadrature" +#samplingM = "quadrature_total" +#samplingM = "random" -if samples == "distributed": +if samples in ["standard", "pivoted", "pole matching"]: radial = 0 # radial = "gaussian" # radial = "thinplate" # radial = "multiquadric" rW0 = 5. - radialWeight = [rW0] * 2 + radialWeight = [rW0] +if samples in ["pivoted", "pole matching"]: + radialM = 0 +# radialM = "gaussian" +# radialM = "thinplate" +# radialM = "multiquadric" + rMW0 = 2. + radialWeightM = [rMW0] assert Delta <= 0 if size == 1: # below mu0 = [40 ** .5, .4] mutar = [45 ** .5, .4] murange = [[30 ** .5, .3], [50 ** .5, .5]] elif size == 2: # top mu0 = [40 ** .5, .6] mutar = [45 ** .5, .6] murange = [[30 ** .5, .5], [50 ** .5, .7]] elif size == 3: # interesting mu0 = [40 ** .5, .5] mutar = [45 ** .5, .5] murange = [[30 ** .5, .3], [50 ** .5, .7]] elif size == 4: # wide_low mu0 = [40 ** .5, .2] mutar = [45 ** .5, .2] murange = [[10 ** .5, .1], [70 ** .5, .3]] elif size == 5: # wide_hi mu0 = [40 ** .5, .8] mutar = [45 ** .5, .8] murange = [[10 ** .5, .7], [70 ** .5, .9]] elif size == 6: # top_zoom mu0 = [50 ** .5, .8] mutar = [55 ** .5, .8] murange = [[40 ** .5, .7], [60 ** .5, .9]] elif size == 7: # huge mu0 = [50 ** .5, .5] mutar = [55 ** .5, .5] murange = [[10 ** .5, .2], [90 ** .5, .8]] +elif size == 11: #L below + mu0 = [110 ** .5, .4] + mutar = [115 ** .5, .4] + murange = [[90 ** .5, .3], [130 ** .5, .5]] +elif size == 12: #L top + mu0 = [110 ** .5, .6] + mutar = [115 ** .5, .6] + murange = [[90 ** .5, .5], [130 ** .5, .7]] +elif size == 13: #L interesting + mu0 = [110 ** .5, .5] + mutar = [115 ** .5, .5] + murange = [[90 ** .5, .3], [130 ** .5, .7]] +elif size == 14: #L belowbelow + mu0 = [110 ** .5, .2] + mutar = [115 ** .5, .2] + murange = [[90 ** .5, .1], [130 ** .5, .3]] +elif size == 15: #L toptop + mu0 = [110 ** .5, .8] + mutar = [115 ** .5, .8] + murange = [[90 ** .5, .7], [130 ** .5, .9]] +elif size == 16: #L interestinginteresting + mu0 = [110 ** .5, .5] + mutar = [115 ** .5, .6] + murange = [[90 ** .5, .1], [130 ** .5, .9]] +elif size == 17: #L interestingtop + mu0 = [110 ** .5, .7] + mutar = [115 ** .5, .6] + murange = [[90 ** .5, .5], [130 ** .5, .9]] +elif size == 18: #L interestingbelow + mu0 = [110 ** .5, .3] + mutar = [115 ** .5, .4] + murange = [[90 ** .5, .1], [130 ** .5, .5]] elif size == 100: # tiny mu0 = [32.5 ** .5, .5] mutar = [34 ** .5, .5] murange = [[30 ** .5, .3], [35 ** .5, .7]] aEff = 1.#25 bEff = 1. - aEff murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5, aEff*murange[0][1] + bEff*murange[1][1]], [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5, aEff*murange[1][1] + bEff*murange[0][1]]] H = 1. L = .75 delta = .05 n = 20 solver = MFE(mu0 = mu0, H = H, L = L, delta = delta, n = n, verbosity = verb) -rescaling = [lambda x: np.power(x, 2.), lambda x: x] -rescalingInv = [lambda x: np.power(x, .5), lambda x: x] +rescalingExp = [2., 1.] if algo == "rational": params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True} - if samples == "distributed": + if samples == "standard": params['polybasis'] = "CHEBYSHEV" # params['polybasis'] = "LEGENDRE" # params['polybasis'] = "MONOMIAL" params['E'] = MN params['radialBasis'] = radial params['radialBasisWeights'] = radialWeight method = RI - elif samples == "centered_fake": + elif samples == "centered": params['polybasis'] = "MONOMIAL" params['S'] = R method = RI - else: - params['S'] = R - method = RP + elif samples in ["pivoted", "pole matching"]: + params['S'] = [SPivot[0]] + params['SMarginal'] = [SPivot[1]] + params['MMarginal'] = MMarginal + params['polybasisPivot'] = "CHEBYSHEV" + params['polybasisMarginal'] = "MONOMIAL" + params['radialBasisPivot'] = radial + params['radialBasisMarginal'] = radialM + params['radialBasisWeightsPivot'] = radialWeight + params['radialBasisWeightsMarginal'] = radialWeightM + if samples == "pivoted": + method = RIP + else: + params['matchingWeight'] = matchingWeight + params['cutOffTolerance'] = cutOffTolerance + params["cutOffType"] = cutOffType + method = RIPM else: #if algo == "RB": params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':S, 'POD':True, 'PODTolerance':PODTol} - if samples == "distributed": - method = RBD - elif samples == "centered_fake": - params['S'] = R - method = RBD - else: + if samples == "standard": + method = RB + elif samples == "centered": params['S'] = R - method = RBC + method = RB + elif samples in ["pivoted", "pole matching"]: + params['S'] = [SPivot[0]] + params['SMarginal'] = [SPivot[1]] + params['MMarginal'] = MMarginal + params['polybasisMarginal'] = "MONOMIAL" + params['radialBasisMarginal'] = radialM + params['radialBasisWeightsMarginal'] = radialWeightM + method = RBP + if samples == "pivoted": + method = RBP + else: + params['matchingWeight'] = matchingWeight + params['cutOffTolerance'] = cutOffTolerance + params["cutOffType"] = cutOffType + method = RBPM -if samples == "distributed": +if samples == "standard": if sampling == "quadrature": - params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling, -# scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling, -# scalingInv = rescalingInv) + params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) params['S'] = [max(j, MN + 1) for j in params['S']] elif sampling == "quadrature_total": - params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) params['S'] = R else: # if sampling == "random": - params['sampler'] = RS(murange, "HALTON", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) params['S'] = R - -elif samples == "centered_fake": - params['sampler'] = MS(murange, points = [mu0], scaling = rescaling, - scalingInv = rescalingInv) +elif samples == "centered": + params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) +elif samples in ["pivoted", "pole matching"]: + if sampling == "quadrature": + params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "CHEBYSHEV") +# params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "GAUSSLEGENDRE") +# params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "UNIFORM") + elif sampling == "quadrature_total": + params['samplerPivot'] = QST([murange[0][0], murange[1][0]], "CHEBYSHEV") + else: # if sampling == "random": + params['samplerPivot'] = RS([murange[0][0], murange[1][0]], "HALTON") + if samplingM == "quadrature": + params['samplerMarginal'] = QS([murange[0][1], murange[1][1]], "UNIFORM") + elif samplingM == "quadrature_total": + params['samplerMarginal'] = QST([murange[0][1], murange[1][1]], "CHEBYSHEV") + else: # if samplingM == "random": + params['samplerMarginal'] = RS([murange[0][1], murange[1][1]], "HALTON") + -approx = method(solver, mu0 = mu0, approxParameters = params, - verbosity = verb, homogeneized = homogeneize) -if samples == "distributed": approx.samplingEngine.allowRepeatedSamples = False +if samples not in ["pivoted", "pole matching"]: + approx = method(solver, mu0 = mu0, approxParameters = params, + verbosity = verb) +else: + approx = method(solver, mu0 = mu0, directionPivot = [0], + approxParameters = params, verbosity = verb) +if samples != "centered": approx.samplingEngine.allowRepeatedSamples = False approx.setupApprox() if show_sample: import ufl import fenics as fen from rrompy.solver.fenics import affine_warping L = mutar[1] y = fen.SpatialCoordinate(solver.V.mesh())[1] warp1, warpI1 = affine_warping(solver.V.mesh(), np.array([[1, 0], [0, 2. * L]])) warp2, warpI2 = affine_warping(solver.V.mesh(), np.array([[1, 0], [0, 2. - 2. * L]])) warp = ufl.conditional(ufl.ge(y, 0.), warp1, warp2) warpI = ufl.conditional(ufl.ge(y, 0.), warpI1, warpI2) approx.plotApprox(mutar, [warp, warpI], name = 'u_app', homogeneized = False, what = "REAL") approx.plotHF(mutar, [warp, warpI], name = 'u_HF', homogeneized = False, what = "REAL") approx.plotErr(mutar, [warp, warpI], name = 'err', homogeneized = False, what = "REAL") # approx.plotRes(mutar, [warp, warpI], name = 'res', # homogeneized = False, what = "REAL") - appErr = approx.normErr(mutar, homogeneized = homogeneize) - solNorm = approx.normHF(mutar, homogeneized = homogeneize) - resNorm = approx.normRes(mutar, homogeneized = homogeneize) - RHSNorm = approx.normRHS(mutar, homogeneized = homogeneize) + appErr = approx.normErr(mutar) + solNorm = approx.normHF(mutar) + resNorm = approx.normRes(mutar) + RHSNorm = approx.normRHS(mutar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) -if algo == "rational" and approx.N > 0: - from plot_zero_set import plotZeroSet2 - muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, - 50, [2., 1.], clip = clip) +approx.verbosity = 5 +from plot_zero_set import plotZeroSet2 +muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, + 200, [2., 1.]) if show_norm: solver._solveBatchSize = 100 from plot_inf_set import plotInfSet2 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( murange, murangeEff, approx, mu0, 50, - [2., 1.], clip = clip, relative = False, - normalizeDen = True) + [2., 1.], relative = True, + nobeta = True) +print(np.sort(approx.getPoles([None, .5]) ** 2.)) diff --git a/examples/2d/pod/fracture_pod_nodomain.py b/examples/2d/pod/fracture_pod_nodomain.py index b9211f4..f43decc 100644 --- a/examples/2d/pod/fracture_pod_nodomain.py +++ b/examples/2d/pod/fracture_pod_nodomain.py @@ -1,190 +1,172 @@ import numpy as np from rrompy.hfengines.linear_problem import MembraneFractureEngineNoDomain \ as MFEND -from rrompy.reduction_methods.centered import RationalPade as RP -from rrompy.reduction_methods.distributed import RationalInterpolant as RI -from rrompy.reduction_methods.centered import RBCentered as RBC -from rrompy.reduction_methods.distributed import RBDistributed as RBD +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) verb = 5 size = 1 show_sample = True show_norm = True ignore_forcing = True ignore_forcing = False clip = -1 #clip = .4 #clip = .6 homogeneize = False #homogeneize = True Delta = 0 MN = 6 R = MN + 1 S = R samples = "centered" -samples = "centered_fake" -samples = "distributed" +samples = "standard" algo = "rational" #algo = "RB" sampling = "quadrature" sampling = "quadrature_total" sampling = "random" -if samples == "distributed": +if samples == "standard": radial = 0 # radial = "gaussian" # radial = "thinplate" # radial = "multiquadric" radialWeight = [2.] assert Delta <= 0 if size == 1: # below mu0Aug = [40 ** .5, .4] mu0 = mu0Aug[0] mutar = 45 ** .5 murange = [[30 ** .5], [50 ** .5]] elif size == 2: # top mu0Aug = [40 ** .5, .6] mu0 = mu0Aug[0] mutar = 45 ** .5 murange = [[30 ** .5], [50 ** .5]] elif size == 3: # interesting mu0Aug = [40 ** .5, .5] mu0 = mu0Aug[0] mutar = 45 ** .5 murange = [[30 ** .5], [50 ** .5]] elif size == 4: # wide_low mu0Aug = [40 ** .5, .2] mu0 = mu0Aug[0] mutar = 45 ** .5 murange = [[10 ** .5], [70 ** .5]] elif size == 5: # wide_hi mu0Aug = [40 ** .5, .8] mu0 = mu0Aug[0] mutar = 45 ** .5 murange = [[10 ** .5], [70 ** .5]] elif size == 6: # top_zoom mu0Aug = [50 ** .5, .8] mu0 = mu0Aug[0] mutar = 55 ** .5 murange = [[40 ** .5], [60 ** .5]] aEff = 1.#25 bEff = 1. - aEff murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5], [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5]] H = 1. L = .75 delta = .05 n = 20 solver = MFEND(mu0 = mu0Aug, H = H, L = L, delta = delta, n = n, verbosity = verb) -rescaling = lambda x: np.power(x, 2.) -rescalingInv = lambda x: np.power(x, .5) +rescalingExp = 2. if algo == "rational": params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True} - if samples == "distributed": + if samples == "standard": params['polybasis'] = "CHEBYSHEV" # params['polybasis'] = "LEGENDRE" # params['polybasis'] = "MONOMIAL" params['E'] = MN params['radialBasis'] = radial params['radialBasisWeights'] = radialWeight - method = RI - elif samples == "centered_fake": + elif samples == "centered": params['polybasis'] = "MONOMIAL" params['S'] = R - method = RI - else: - params['S'] = R - method = RP + method = RI else: #if algo == "RB": params = {'R':R, 'S':S, 'POD':True} - if samples == "distributed": - method = RBD - elif samples == "centered_fake": - params['S'] = R - method = RBD - else: + if samples == "centered": params['S'] = R - method = RBC + method = RB -if samples == "distributed": +if samples == "standard": if sampling == "quadrature": - params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling, -# scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling, -# scalingInv = rescalingInv) + params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) params['S'] = [max(j, MN + 1) for j in params['S']] elif sampling == "quadrature_total": - params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) params['S'] = R else: # if sampling == "random": - params['sampler'] = RS(murange, "HALTON", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) params['S'] = R -elif samples == "centered_fake": - params['sampler'] = MS(murange, points = [mu0], scaling = rescaling, - scalingInv = rescalingInv) +elif samples == "centered": + params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb, homogeneized = homogeneize) -if samples == "distributed": approx.samplingEngine.allowRepeatedSamples = False +if samples == "standard": approx.samplingEngine.allowRepeatedSamples = False approx.setupApprox() if show_sample: import ufl import fenics as fen from rrompy.solver.fenics import affine_warping L = solver.lFrac y = fen.SpatialCoordinate(solver.V.mesh())[1] warp1, warpI1 = affine_warping(solver.V.mesh(), np.array([[1, 0], [0, 2. * L]])) warp2, warpI2 = affine_warping(solver.V.mesh(), np.array([[1, 0], [0, 2. - 2. * L]])) warp = ufl.conditional(ufl.ge(y, 0.), warp1, warp2) warpI = ufl.conditional(ufl.ge(y, 0.), warpI1, warpI2) approx.plotApprox(mutar, [warp, warpI], name = 'u_app', homogeneized = False, what = "REAL") approx.plotHF(mutar, [warp, warpI], name = 'u_HF', homogeneized = False, what = "REAL") approx.plotErr(mutar, [warp, warpI], name = 'err', homogeneized = False, what = "REAL") # approx.plotRes(mutar, [warp, warpI], name = 'res', # homogeneized = False, what = "REAL") appErr = approx.normErr(mutar, homogeneized = homogeneize) solNorm = approx.normHF(mutar, homogeneized = homogeneize) resNorm = approx.normRes(mutar, homogeneized = homogeneize) RHSNorm = approx.normRHS(mutar, homogeneized = homogeneize) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) if algo == "rational": from plot_zero_set import plotZeroSet1 muZeroVals, Qvals = plotZeroSet1(murange, murangeEff, approx, mu0, 1000, 2.) if show_norm: solver._solveBatchSize = 10 from plot_inf_set import plotInfSet1 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet1( murange, murangeEff, approx, mu0, 200, 2., relative = False, normalizeDen = True) +print(approx.getPoles() ** 2.) diff --git a/examples/2d/pod/matrix_passive_pod.py b/examples/2d/pod/matrix_passive_pod.py new file mode 100644 index 0000000..7d3e9d7 --- /dev/null +++ b/examples/2d/pod/matrix_passive_pod.py @@ -0,0 +1,209 @@ +import numpy as np +from rrompy.hfengines.linear_problem.tridimensional import \ + MatrixDynamicalPassive as MDP +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB +from rrompy.reduction_methods.pivoted import RationalInterpolantPivoted as RIP +from rrompy.reduction_methods.pivoted import ReducedBasisPivoted as RBP +from rrompy.reduction_methods.pole_matching import \ + RationalInterpolantPoleMatching as RIPM +from rrompy.reduction_methods.pole_matching import \ + ReducedBasisPoleMatching as RBPM +from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, + QuadratureSamplerTotal as QST, + ManualSampler as MS, + RandomSampler as RS) + +verb = 10 +size = 3 +show_sample = True +show_norm = True + +Delta = 0 +MN = 7 +R = (MN + 2) * (MN + 1) // 2 +S = [int(np.ceil(R ** .5))] * 2 +PODTol = 1e-6 +SPivot = [MN + 1, 3] +MMarginal = SPivot[1] - 1 + +samples = "centered" +samples = "standard" +samples = "pivoted" +samples = "pole matching" +algo = "rational" +#algo = "RB" +sampling = "quadrature" +#sampling = "quadrature_total" +#sampling = "random" +samplingM = "quadrature" +#samplingM = "quadrature_total" +#samplingM = "random" + +if samples in ["standard", "pivoted", "pole matching"]: + radial = 0 +# radial = "gaussian" +# radial = "thinplate" +# radial = "multiquadric" + rW0 = 10. + radialWeight = [rW0] +if samples in ["pivoted", "pole matching"]: + radialM = 0 +# radialM = "gaussian" +# radialM = "thinplate" +# radialM = "multiquadric" + rMW0 = 2. + radialWeightM = [rMW0] +matchingWeight = 1. +cutOffTolerance = 5. +cutOffType = "POTENTIAL" + +if size == 1: + mu0 = [2.7e2, 20] + mutar = [3e2, 25] + murange = [[20., 10], [5.2e2, 30]] +elif size == 2: + mu0 = [2.7e2, 60] + mutar = [3e2, 75] + murange = [[20., 10], [5.2e2, 110]] +elif size == 3: + mu0 = [2.7e2, 160] + mutar = [3e2, 105] + murange = [[20., 10], [5.2e2, 310]] + +assert Delta <= 0 + +aEff = 1.#25 +bEff = 1. - aEff +murangeEff = [[aEff*murange[0][0] + bEff*murange[1][0], + aEff*murange[0][1] + bEff*murange[1][1]], + [aEff*murange[1][0] + bEff*murange[0][0], + aEff*murange[1][1] + bEff*murange[0][1]]] + +n = 100 +b = 10 + +solver = MDP(mu0 = mu0, n = n, b = b, verbosity = verb) + +if algo == "rational": + params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True} + if samples == "standard": + params['polybasis'] = "CHEBYSHEV" +# params['polybasis'] = "LEGENDRE" +# params['polybasis'] = "MONOMIAL" + params['E'] = MN + params['radialBasis'] = radial + params['radialBasisWeights'] = radialWeight + method = RI + elif samples == "centered": + params['polybasis'] = "MONOMIAL" + params['S'] = R + method = RI + elif samples in ["pivoted", "pole matching"]: + params['S'] = [SPivot[0]] + params['SMarginal'] = [SPivot[1]] + params['MMarginal'] = MMarginal + params['polybasisPivot'] = "CHEBYSHEV" + params['polybasisMarginal'] = "MONOMIAL" + params['radialBasisPivot'] = radial + params['radialBasisMarginal'] = radialM + params['radialBasisWeightsPivot'] = radialWeight + params['radialBasisWeightsMarginal'] = radialWeightM + if samples == "pivoted": + method = RIP + else: + params['matchingWeight'] = matchingWeight + params['cutOffTolerance'] = cutOffTolerance + params["cutOffType"] = cutOffType + method = RIPM +else: #if algo == "RB": + params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':S, + 'POD':True, 'PODTolerance':PODTol} + if samples == "standard": + method = RB + elif samples == "centered": + params['S'] = R + method = RB + elif samples in ["pivoted", "pole matching"]: + params['R'] = SPivot[0] + params['S'] = [SPivot[0]] + params['SMarginal'] = [SPivot[1]] + params['MMarginal'] = MMarginal + params['polybasisMarginal'] = "MONOMIAL" + params['radialBasisMarginal'] = radialM + params['radialBasisWeightsMarginal'] = radialWeightM + if samples == "pivoted": + method = RBP + else: + params['matchingWeight'] = matchingWeight + params['cutOffTolerance'] = cutOffTolerance + params["cutOffType"] = cutOffType + method = RBPM + +if samples == "standard": + if sampling == "quadrature": + params['sampler'] = QS(murange, "CHEBYSHEV") +# params['sampler'] = QS(murange, "GAUSSLEGENDRE") +# params['sampler'] = QS(murange, "UNIFORM") + elif sampling == "quadrature_total": + params['sampler'] = QST(murange, "CHEBYSHEV") + else: # if sampling == "random": + params['sampler'] = RS(murange, "HALTON") +elif samples == "centered": + params['sampler'] = MS(murange, points = [mu0]) +elif samples in ["pivoted", "pole matching"]: + if sampling == "quadrature": + params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "CHEBYSHEV") +# params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "GAUSSLEGENDRE") +# params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "UNIFORM") + elif sampling == "quadrature_total": + params['samplerPivot'] = QST([murange[0][0], murange[1][0]], "CHEBYSHEV") + else: # if sampling == "random": + params['samplerPivot'] = RS([murange[0][0], murange[1][0]], "HALTON") + if samplingM == "quadrature": + params['samplerMarginal'] = QS([murange[0][1], murange[1][1]], "UNIFORM") + elif samplingM == "quadrature_total": + params['samplerMarginal'] = QST([murange[0][1], murange[1][1]], "CHEBYSHEV") + else: # if samplingM == "random": + params['samplerMarginal'] = RS([murange[0][1], murange[1][1]], "HALTON") + +if samples not in ["pivoted", "pole matching"]: + approx = method(solver, mu0 = mu0, approxParameters = params, + verbosity = verb) +else: + approx = method(solver, mu0 = mu0, directionPivot = [0], + approxParameters = params, verbosity = verb) +if samples != "centered": approx.samplingEngine.allowRepeatedSamples = False + +approx.setupApprox() +if show_sample: + L = mutar[1] + approx.plotApprox(mutar, name = 'u_app', homogeneized = False, + what = "REAL") + approx.plotHF(mutar, name = 'u_HF', homogeneized = False, what = "REAL") + approx.plotErr(mutar, name = 'err', homogeneized = False, what = "REAL") +# approx.plotRes(mutar, name = 'res', homogeneized = False, what = "REAL") + appErr = approx.normErr(mutar) + solNorm = approx.normHF(mutar) + resNorm = approx.normRes(mutar) + RHSNorm = approx.normRHS(mutar) + print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, + np.divide(appErr, solNorm))) + print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, + np.divide(resNorm, RHSNorm))) + +try: + from plot_zero_set import plotZeroSet2 + muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, + 200, [1., 1], polesImTol = 2.) +except: pass + +if show_norm: + solver._solveBatchSize = 100 + from plot_inf_set import plotInfSet2 + muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( + murange, murangeEff, approx, mu0, 50, + [1., 1.], relative = False, + nobeta = True) + +print(1.j * approx.getPoles([None, 50.])) diff --git a/examples/2d/pod/plot_inf_set.py b/examples/2d/pod/plot_inf_set.py index 8c01fc2..d7ba227 100644 --- a/examples/2d/pod/plot_inf_set.py +++ b/examples/2d/pod/plot_inf_set.py @@ -1,323 +1,335 @@ import warnings import numpy as np from matplotlib import pyplot as plt def plotInfSet1FromData(mus, Z, T, R, E, beta, murange, approx, mu0, exp = 2., normalizeDen = False): if hasattr(approx, "mus"): mu2x = approx.mus(0) ** exp else: mu2x = mu0[0] ** exp murangeExp = [[murange[0][0] ** exp], [murange[1][0] ** exp]] mu1 = np.real(np.power(mus, exp)) - eta = R / beta / E ZTmin, ZTmax = min(np.min(Z), np.min(T)), max(np.max(Z), np.max(T)) Rmin, Rmax = np.min(R), np.max(R) Emin, Emax = np.min(E), np.max(E) - betamin, betamax = np.min(beta), np.max(beta) - etamin, etamax = np.min(eta), np.max(eta) + if not np.isnan(beta[0]): + eta = R / beta / E + betamin, betamax = np.min(beta), np.max(beta) + etamin, etamax = np.min(eta), np.max(eta) plt.figure(figsize = (15, 7)) plt.jet() plt.semilogy(mu1, Z) plt.semilogy(mu1, T, '--') for l_ in approx.trainedModel.getPoles(): plt.plot([np.real(l_ ** exp)] * 2, [ZTmin, ZTmax], 'b:') - plt.plot(mu2x, [ZTmin] * len(mu2x), 'kx') + plt.plot(np.real(mu2x), [ZTmin] * len(mu2x), 'kx') plt.plot([murangeExp[0][0]] * 2, [ZTmin, ZTmax], 'm:') plt.plot([murangeExp[1][0]] * 2, [ZTmin, ZTmax], 'm:') plt.xlim(mu1[0], mu1[-1]) plt.title("|u(mu)|, |u_app(mu)|") plt.grid() plt.show() plt.figure(figsize = (15, 7)) plt.jet() plt.semilogy(mu1, R) for l_ in approx.trainedModel.getPoles(): plt.plot([np.real(l_ ** exp)] * 2, [Rmin, Rmax], 'b:') - plt.plot(mu2x, [Rmax] * len(mu2x), 'kx') + plt.plot(np.real(mu2x), [Rmax] * len(mu2x), 'kx') plt.plot([murangeExp[0][0]] * 2, [Rmin, Rmax], 'm:') plt.plot([murangeExp[1][0]] * 2, [Rmin, Rmax], 'm:') plt.xlim(mu1[0], mu1[-1]) if normalizeDen: plt.title("|Q(mu)res(mu)|") else: plt.title("|res(mu)|") plt.grid() plt.show() plt.figure(figsize = (15, 7)) plt.jet() plt.semilogy(mu1, E) for l_ in approx.trainedModel.getPoles(): plt.plot([np.real(l_ ** exp)] * 2, [Emin, Emax], 'b:') - plt.plot(mu2x, [Emax] * len(mu2x), 'kx') + plt.plot(np.real(mu2x), [Emax] * len(mu2x), 'kx') plt.plot([murangeExp[0][0]] * 2, [Emin, Emax], 'm:') plt.plot([murangeExp[1][0]] * 2, [Emin, Emax], 'm:') plt.xlim(mu1[0], mu1[-1]) if normalizeDen: plt.title("|Q(mu)err(mu)|") else: plt.title("|err(mu)|") plt.grid() plt.show() - plt.figure(figsize = (15, 7)) - plt.jet() - plt.semilogy(mu1, beta) - for l_ in approx.trainedModel.getPoles(): - plt.plot([np.real(l_ ** exp)] * 2, [betamin, betamax], 'b:') - plt.plot(mu2x, [betamax] * len(mu2x), 'kx') - plt.plot([murangeExp[0][0]] * 2, [betamin, betamax], 'm:') - plt.plot([murangeExp[1][0]] * 2, [betamin, betamax], 'm:') - plt.xlim(mu1[0], mu1[-1]) - plt.title("beta(mu)") - plt.grid() - plt.show() + if not np.isnan(beta[0]): + plt.figure(figsize = (15, 7)) + plt.jet() + plt.plot(mu1, beta) + for l_ in approx.trainedModel.getPoles(): + plt.plot([np.real(l_ ** exp)] * 2, [betamin, betamax], 'b:') + plt.plot(np.real(mu2x), [betamax] * len(mu2x), 'kx') + plt.plot([murangeExp[0][0]] * 2, [betamin, betamax], 'm:') + plt.plot([murangeExp[1][0]] * 2, [betamin, betamax], 'm:') + plt.xlim(mu1[0], mu1[-1]) + plt.title("beta(mu)") + plt.grid() + plt.show() - plt.figure(figsize = (15, 7)) - plt.jet() - plt.semilogy(mu1, R / beta) - plt.semilogy(mu1, E, '--') - for l_ in approx.trainedModel.getPoles(): - plt.plot([np.real(l_ ** exp)] * 2, [Emin, Emax], 'b:') - plt.plot(mu2x, [Emax] * len(mu2x), 'kx') - plt.plot([murangeExp[0][0]] * 2, [Emin, Emax], 'm:') - plt.plot([murangeExp[1][0]] * 2, [Emin, Emax], 'm:') - plt.xlim(mu1[0], mu1[-1]) - if normalizeDen: - plt.title("|Q(mu)res(mu)/beta(mu)|") - else: - plt.title("|res(mu)/beta(mu)|") - plt.grid() - plt.show() - - plt.figure(figsize = (15, 7)) - plt.jet() - plt.semilogy(mu1, eta) - for l_ in approx.trainedModel.getPoles(): - plt.plot([np.real(l_ ** exp)] * 2, [etamin, etamax], 'b:') - plt.plot(mu2x, [etamax] * len(mu2x), 'kx') - plt.plot([murangeExp[0][0]] * 2, [etamin, etamax], 'm:') - plt.plot([murangeExp[1][0]] * 2, [etamin, etamax], 'm:') - plt.xlim(mu1[0], mu1[-1]) - plt.title("eta(mu)") - plt.grid() - plt.show() + plt.figure(figsize = (15, 7)) + plt.jet() + plt.semilogy(mu1, R / beta) + plt.semilogy(mu1, E, '--') + for l_ in approx.trainedModel.getPoles(): + plt.plot([np.real(l_ ** exp)] * 2, [Emin, Emax], 'b:') + plt.plot(np.real(mu2x), [Emax] * len(mu2x), 'kx') + plt.plot([murangeExp[0][0]] * 2, [Emin, Emax], 'm:') + plt.plot([murangeExp[1][0]] * 2, [Emin, Emax], 'm:') + plt.xlim(mu1[0], mu1[-1]) + if normalizeDen: + plt.title("|Q(mu)res(mu)/beta(mu)|") + else: + plt.title("|res(mu)/beta(mu)|") + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + plt.semilogy(mu1, eta) + for l_ in approx.trainedModel.getPoles(): + plt.plot([np.real(l_ ** exp)] * 2, [etamin, etamax], 'b:') + plt.plot(np.real(mu2x), [etamax] * len(mu2x), 'kx') + plt.plot([murangeExp[0][0]] * 2, [etamin, etamax], 'm:') + plt.plot([murangeExp[1][0]] * 2, [etamin, etamax], 'm:') + plt.xlim(mu1[0], mu1[-1]) + plt.title("eta(mu)") + plt.grid() + plt.show() def plotInfSet1(murange, murangeEff, approx, mu0, nSamples = 20, exp = 2., - relative = True, normalizeDen = False): + relative = True, normalizeDen = False, nobeta = False): mu1 = np.linspace(murangeEff[0][0] ** exp, murangeEff[1][0] ** exp, nSamples) mus = np.power(mu1, 1. / exp) Z = approx.normHF(mus) T = approx.normApprox(mus) R = approx.normRes(mus) E = approx.normErr(mus) if relative: F = approx.normRHS(mus) R /= F E /= Z if normalizeDen: Qvals = np.abs(approx.trainedModel.getQVal(mus)) R *= Qvals E *= Qvals - beta = approx.HFEngine.stabilityFactor(approx.getHF(mus), mus) + if nobeta: + beta = np.empty(len(mus)) + beta[:] = np.nan + else: + beta = approx.HFEngine.stabilityFactor(approx.getHF(mus), mus) plotInfSet1FromData(mus, Z, T, R, E, beta, murange, approx, mu0, exp, normalizeDen) return mus, Z, T, R, E, beta -def plotInfSet2FromData(mus, Ze, Te, Re, Ee, betae, murange, approx, mu0, +def plotInfSet2FromData(mus, Ze, Te, Re, Ee, beta, murange, approx, mu0, exps = [2., 2.], clip = -1, normalizeDen = False): if hasattr(approx, "mus"): mu2x, mu2y = approx.mus(0) ** exps[0], approx.mus(1) ** exps[1] else: mu2x, mu2y = mu0[0] ** exps[0], mu0[1] ** exps[1] murangeExp = [[murange[0][0] ** exps[0], murange[0][1] ** exps[1]], [murange[1][0] ** exps[0], murange[1][1] ** exps[1]]] mu1s = np.unique([m[0] for m in mus]) mu2s = np.unique([m[1] for m in mus]) mu1 = np.power(mu1s, exps[0]) mu2 = np.power(mu2s, exps[1]) Mu1, Mu2 = np.meshgrid(np.real(mu1), np.real(mu2)) Z = np.log10(Ze) T = np.log10(Te) R = np.log10(Re) E = np.log10(Ee) - beta = np.log10(betae) ZTmin, ZTmax = min(np.min(Z), np.min(T)), max(np.max(Z), np.max(T)) Rmin, Rmax = np.min(R), np.max(R) Emin, Emax = np.min(E), np.max(E) - betamin, betamax = np.min(beta), np.max(beta) + if not np.isnan(beta[0, 0]): + betamin, betamax = np.min(beta), np.max(beta) if clip > 0: ZTmax -= clip * (ZTmax - ZTmin) cmap = plt.cm.bone else: cmap = plt.cm.jet warnings.simplefilter("ignore", category = UserWarning) plt.figure(figsize = (15, 7)) plt.jet() p = plt.contourf(Mu1, Mu2, Z, cmap = cmap, levels = np.linspace(ZTmin, ZTmax, 50)) if clip > 0: plt.contour(Mu1, Mu2, Z, [ZTmin]) - plt.plot(mu2x, mu2y, 'kx') + plt.plot(np.real(mu2x), np.real(mu2y), 'kx') plt.plot([murangeExp[0][0]] * 2, [murangeExp[0][1], murangeExp[1][1]], 'm:') plt.plot([murangeExp[0][0], murangeExp[1][0]], [murangeExp[1][1]] * 2, 'm:') plt.plot([murangeExp[1][0]] * 2, [murangeExp[1][1], murangeExp[0][1]], 'm:') plt.plot([murangeExp[1][0], murangeExp[0][0]], [murangeExp[0][1]] * 2, 'm:') plt.colorbar(p) plt.title("log10|u(mu)|") plt.grid() plt.show() plt.figure(figsize = (15, 7)) plt.jet() p = plt.contourf(Mu1, Mu2, T, cmap = cmap, levels = np.linspace(ZTmin, ZTmax, 50)) if clip > 0: plt.contour(Mu1, Mu2, T, [ZTmin]) - plt.plot(mu2x, mu2y, 'kx') + plt.plot(np.real(mu2x), np.real(mu2y), 'kx') plt.plot([murangeExp[0][0]] * 2, [murangeExp[0][1], murangeExp[1][1]], 'm:') plt.plot([murangeExp[0][0], murangeExp[1][0]], [murangeExp[1][1]] * 2, 'm:') plt.plot([murangeExp[1][0]] * 2, [murangeExp[1][1], murangeExp[0][1]], 'm:') plt.plot([murangeExp[1][0], murangeExp[0][0]], [murangeExp[0][1]] * 2, 'm:') plt.title("log10|u_app(mu)|") plt.colorbar(p) plt.grid() plt.show() plt.figure(figsize = (15, 7)) plt.jet() p = plt.contourf(Mu1, Mu2, R, levels = np.linspace(Rmin, Rmax, 50)) - plt.plot(mu2x, mu2y, 'kx') + plt.plot(np.real(mu2x), np.real(mu2y), 'kx') plt.plot([murangeExp[0][0]] * 2, [murangeExp[0][1], murangeExp[1][1]], 'm:') plt.plot([murangeExp[0][0], murangeExp[1][0]], [murangeExp[1][1]] * 2, 'm:') plt.plot([murangeExp[1][0]] * 2, [murangeExp[1][1], murangeExp[0][1]], 'm:') plt.plot([murangeExp[1][0], murangeExp[0][0]], [murangeExp[0][1]] * 2, 'm:') if normalizeDen: plt.title("log10|Q(mu)res(mu)|") else: plt.title("log10|res(mu)|") plt.colorbar(p) plt.grid() plt.show() plt.figure(figsize = (15, 7)) plt.jet() p = plt.contourf(Mu1, Mu2, E, levels = np.linspace(Emin, Emax, 50)) - plt.plot(mu2x, mu2y, 'kx') + plt.plot(np.real(mu2x), np.real(mu2y), 'kx') plt.plot([murangeExp[0][0]] * 2, [murangeExp[0][1], murangeExp[1][1]], 'm:') plt.plot([murangeExp[0][0], murangeExp[1][0]], [murangeExp[1][1]] * 2, 'm:') plt.plot([murangeExp[1][0]] * 2, [murangeExp[1][1], murangeExp[0][1]], 'm:') plt.plot([murangeExp[1][0], murangeExp[0][0]], [murangeExp[0][1]] * 2, 'm:') if normalizeDen: plt.title("log10|Q(mu)err(mu)|") else: plt.title("log10|err(mu)|") plt.colorbar(p) plt.grid() plt.show() - plt.figure(figsize = (15, 7)) - plt.jet() - p = plt.contourf(Mu1, Mu2, beta, - levels = np.linspace(betamin, betamax, 50)) - plt.plot(mu2x, mu2y, 'kx') - plt.plot([murangeExp[0][0]] * 2, - [murangeExp[0][1], murangeExp[1][1]], 'm:') - plt.plot([murangeExp[0][0], murangeExp[1][0]], - [murangeExp[1][1]] * 2, 'm:') - plt.plot([murangeExp[1][0]] * 2, - [murangeExp[1][1], murangeExp[0][1]], 'm:') - plt.plot([murangeExp[1][0], murangeExp[0][0]], - [murangeExp[0][1]] * 2, 'm:') - plt.title("log10(beta(mu))") - plt.colorbar(p) - plt.grid() - plt.show() - - plt.figure(figsize = (15, 7)) - plt.jet() - p = plt.contourf(Mu1, Mu2, R - beta, levels = np.linspace(Emin, Emax, 50)) - plt.plot(mu2x, mu2y, 'kx') - plt.plot([murangeExp[0][0]] * 2, - [murangeExp[0][1], murangeExp[1][1]], 'm:') - plt.plot([murangeExp[0][0], murangeExp[1][0]], - [murangeExp[1][1]] * 2, 'm:') - plt.plot([murangeExp[1][0]] * 2, - [murangeExp[1][1], murangeExp[0][1]], 'm:') - plt.plot([murangeExp[1][0], murangeExp[0][0]], - [murangeExp[0][1]] * 2, 'm:') - if normalizeDen: - plt.title("log10|Q(mu)res(mu)/beta(mu)|") - else: - plt.title("log10|res(mu)/beta(mu)|") - plt.colorbar(p) - plt.grid() - plt.show() - - plt.figure(figsize = (15, 7)) - plt.jet() - p = plt.contourf(Mu1, Mu2, R - beta - E, 50) - plt.plot(mu2x, mu2y, 'kx') - plt.plot([murangeExp[0][0]] * 2, - [murangeExp[0][1], murangeExp[1][1]], 'm:') - plt.plot([murangeExp[0][0], murangeExp[1][0]], - [murangeExp[1][1]] * 2, 'm:') - plt.plot([murangeExp[1][0]] * 2, - [murangeExp[1][1], murangeExp[0][1]], 'm:') - plt.plot([murangeExp[1][0], murangeExp[0][0]], - [murangeExp[0][1]] * 2, 'm:') - plt.title("log10|eta(mu)|") - plt.colorbar(p) - plt.grid() - plt.show() + if not np.isnan(beta[0, 0]): + plt.figure(figsize = (15, 7)) + plt.jet() + p = plt.contourf(Mu1, Mu2, beta, + levels = np.linspace(betamin, betamax, 50)) + plt.plot(np.real(mu2x), np.real(mu2y), 'kx') + plt.plot([murangeExp[0][0]] * 2, + [murangeExp[0][1], murangeExp[1][1]], 'm:') + plt.plot([murangeExp[0][0], murangeExp[1][0]], + [murangeExp[1][1]] * 2, 'm:') + plt.plot([murangeExp[1][0]] * 2, + [murangeExp[1][1], murangeExp[0][1]], 'm:') + plt.plot([murangeExp[1][0], murangeExp[0][0]], + [murangeExp[0][1]] * 2, 'm:') + plt.title("beta(mu)") + plt.colorbar(p) + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + p = plt.contourf(Mu1, Mu2, R - np.log10(beta), + levels = np.linspace(Emin, Emax, 50)) + plt.plot(np.real(mu2x), np.real(mu2y), 'kx') + plt.plot([murangeExp[0][0]] * 2, + [murangeExp[0][1], murangeExp[1][1]], 'm:') + plt.plot([murangeExp[0][0], murangeExp[1][0]], + [murangeExp[1][1]] * 2, 'm:') + plt.plot([murangeExp[1][0]] * 2, + [murangeExp[1][1], murangeExp[0][1]], 'm:') + plt.plot([murangeExp[1][0], murangeExp[0][0]], + [murangeExp[0][1]] * 2, 'm:') + if normalizeDen: + plt.title("log10|Q(mu)res(mu)/beta(mu)|") + else: + plt.title("log10|res(mu)/beta(mu)|") + plt.colorbar(p) + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + p = plt.contourf(Mu1, Mu2, R - np.log10(beta) - E, 50) + plt.plot(np.real(mu2x), np.real(mu2y), 'kx') + plt.plot([murangeExp[0][0]] * 2, + [murangeExp[0][1], murangeExp[1][1]], 'm:') + plt.plot([murangeExp[0][0], murangeExp[1][0]], + [murangeExp[1][1]] * 2, 'm:') + plt.plot([murangeExp[1][0]] * 2, + [murangeExp[1][1], murangeExp[0][1]], 'm:') + plt.plot([murangeExp[1][0], murangeExp[0][0]], + [murangeExp[0][1]] * 2, 'm:') + plt.title("log10|eta(mu)|") + plt.colorbar(p) + plt.grid() + plt.show() def plotInfSet2(murange, murangeEff, approx, mu0, nSamples = 200, exps = [2., 2.], clip = -1, relative = True, - normalizeDen = False): + normalizeDen = False, nobeta = False): mu1 = np.linspace(murangeEff[0][0] ** exps[0], murangeEff[1][0] ** exps[0], nSamples) mu2 = np.linspace(murangeEff[0][1] ** exps[1], murangeEff[1][1] ** exps[1], nSamples) mu1s = np.power(mu1, 1. / exps[0]) mu2s = np.power(mu2, 1. / exps[1]) mus = [(m1, m2) for m2 in mu2s for m1 in mu1s] Ze = approx.normHF(mus).reshape((nSamples, nSamples)) Te = approx.normApprox(mus).reshape((nSamples, nSamples)) Re = approx.normRes(mus).reshape((nSamples, nSamples)) Ee = approx.normErr(mus).reshape((nSamples, nSamples)) if relative: Fe = approx.normRHS(mus).reshape((nSamples, nSamples)) Re /= Fe Ee /= Ze if normalizeDen: Qvals = np.abs(approx.trainedModel.getQVal(mus).reshape( (nSamples, nSamples))) Re *= Qvals Ee *= Qvals - betae = approx.HFEngine.stabilityFactor(approx.getHF(mus), mus).reshape( - (nSamples, nSamples)) + if nobeta: + betae = np.empty((nSamples, nSamples)) + betae[:, :] = np.nan + else: + betae = approx.HFEngine.stabilityFactor(approx.getHF(mus), mus)\ + .reshape((nSamples, nSamples)) plotInfSet2FromData(mus, Ze, Te, Re, Ee, betae, murange, approx, mu0, exps, clip, normalizeDen) return mus, Ze, Te, Re, Ee, betae diff --git a/examples/2d/pod/plot_zero_set.py b/examples/2d/pod/plot_zero_set.py index 69b32ed..825bf66 100644 --- a/examples/2d/pod/plot_zero_set.py +++ b/examples/2d/pod/plot_zero_set.py @@ -1,80 +1,93 @@ import warnings import numpy as np from matplotlib import pyplot as plt def plotZeroSet1(murange, murangeEff, approx, mu0, nSamples = 200, exp = 2.): if hasattr(approx, "mus"): mu2x = approx.mus(0) ** exp else: mu2x = mu0[0] ** exp murangeExp = [[murange[0][0] ** exp], [murange[1][0] ** exp]] mu1 = np.linspace(murangeEff[0][0] ** exp, murangeEff[1][0] ** exp, nSamples) mus = np.power(mu1, 1. / exp) mu1 = np.real(mu1) Z = approx.trainedModel.getQVal(mus) Zabs = np.abs(Z) Zmin, Zmax = np.min(Zabs), np.max(Zabs) plt.figure(figsize = (15, 7)) plt.jet() plt.semilogy(mu1, Zabs) for l_ in approx.trainedModel.getPoles(): plt.plot([np.real(l_ ** exp)] * 2, [Zmin, Zmax], 'b--') plt.plot(mu2x, [Zmax] * len(mu2x), 'kx') plt.plot([murangeExp[0][0]] * 2, [Zmin, Zmax], 'm:') plt.plot([murangeExp[1][0]] * 2, [Zmin, Zmax], 'm:') plt.xlim(mu1[0], mu1[-1]) plt.title("|Q(mu)|") plt.grid() plt.show() return mus, Z def plotZeroSet2(murange, murangeEff, approx, mu0, nSamples = 200, - exps = [2., 2.], clip = -1): + exps = [2., 2.], clip = -1, polesImTol : float = 1e-5): if hasattr(approx, "mus"): mu2x, mu2y = approx.mus(0) ** exps[0], approx.mus(1) ** exps[1] else: mu2x, mu2y = mu0[0] ** exps[0], mu0[1] ** exps[1] murangeExp = [[murange[0][0] ** exps[0], murange[0][1] ** exps[1]], [murange[1][0] ** exps[0], murange[1][1] ** exps[1]]] mu1 = np.linspace(murangeEff[0][0] ** exps[0], murangeEff[1][0] ** exps[0], nSamples) mu2 = np.linspace(murangeEff[0][1] ** exps[1], murangeEff[1][1] ** exps[1], nSamples) mu1s = np.power(mu1, 1. / exps[0]) mu2s = np.power(mu2, 1. / exps[1]) Mu1, Mu2 = np.meshgrid(np.real(mu1), np.real(mu2)) mus = [(m1, m2) for m2 in mu2s for m1 in mu1s] - Z = approx.trainedModel.getQVal(mus).reshape(Mu1.shape) - Zabs = np.log10(np.abs(Z)) - Zabsmin, Zabsmax = np.min(Zabs), np.max(Zabs) - if clip > 0: - Zabsmin += clip * (Zabsmax - Zabsmin) - cmap = plt.cm.bone_r - else: + poles1, poles2 = [], [] + for m2 in mu2s: + pls = approx.getPoles([None, m2]) ** exps[0] + pls[np.imag(pls) > polesImTol] = np.nan + pls = np.real(pls) + poles1 += list(pls) + poles2 += [m2] * len(pls) + try: + Z = approx.trainedModel.getQVal(mus).reshape(Mu1.shape) + Zabs = np.log10(np.abs(Z)) + Zabsmin, Zabsmax = np.min(Zabs), np.max(Zabs) + if clip > 0: + Zabsmin += clip * (Zabsmax - Zabsmin) + cmap = plt.cm.bone_r + else: + cmap = plt.cm.jet + except: + Z = None cmap = plt.cm.jet warnings.simplefilter("ignore", category = UserWarning) plt.figure(figsize = (15, 7)) plt.jet() - p = plt.contourf(Mu1, Mu2, Zabs, cmap = cmap, - levels = np.linspace(Zabsmin, Zabsmax, 50)) - if clip > 0: - plt.contour(Mu1, Mu2, Zabs, [Zabsmin]) - plt.contour(Mu1, Mu2, np.real(Z), [0.], linestyles = 'dashed') - plt.contour(Mu1, Mu2, np.imag(Z), [0.], linewidths = 1, - linestyles = 'dotted') - plt.plot(mu2x, mu2y, 'kx') + if Z is not None: + p = plt.contourf(Mu1, Mu2, Zabs, cmap = cmap, + levels = np.linspace(Zabsmin, Zabsmax, 50)) + if clip > 0: + plt.contour(Mu1, Mu2, Zabs, [Zabsmin]) + plt.plot(poles1, poles2, 'k.') + plt.plot(np.real(mu2x), np.real(mu2y), 'kx') plt.plot([murangeExp[0][0]] * 2, [murangeExp[0][1], murangeExp[1][1]], 'm:') plt.plot([murangeExp[0][0], murangeExp[1][0]], [murangeExp[1][1]] * 2, 'm:') plt.plot([murangeExp[1][0]] * 2, [murangeExp[1][1], murangeExp[0][1]], 'm:') plt.plot([murangeExp[1][0], murangeExp[0][0]], [murangeExp[0][1]] * 2, 'm:') - plt.colorbar(p) + if Z is not None: + plt.colorbar(p) + plt.xlim(murangeExp[0][0], murangeExp[1][0]) + plt.ylim(murangeExp[0][1], murangeExp[1][1]) plt.title("log10|Q(mu)|") plt.grid() plt.show() return mus, Z diff --git a/examples/2d/pod/square_pod.py b/examples/2d/pod/square_pod.py index c9768d0..ecdb2f3 100644 --- a/examples/2d/pod/square_pod.py +++ b/examples/2d/pod/square_pod.py @@ -1,152 +1,194 @@ import numpy as np from rrompy.hfengines.linear_problem.bidimensional import \ HelmholtzSquareDomainProblemEngine as HSDPE -from rrompy.reduction_methods.centered import RationalPade as RP -from rrompy.reduction_methods.distributed import RationalInterpolant as RI -from rrompy.reduction_methods.centered import RBCentered as RBC -from rrompy.reduction_methods.distributed import RBDistributed as RBD +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB +from rrompy.reduction_methods.pole_matching import \ + RationalInterpolantPoleMatching as RIPM +from rrompy.reduction_methods.pole_matching import \ + ReducedBasisPoleMatching as RBPM from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) verb = 5 -size = 1 +size = 7 show_sample = False show_norm = True ignore_forcing = True ignore_forcing = False clip = -1 #clip = .4 #clip = .6 MN = 6 R = (MN + 2) * (MN + 1) // 2 S = [int(np.ceil(R ** .5))] * 2 +SPivot = [MN + 1, 3] +MMarginal = SPivot[1] - 2 +matchingWeight = 10. samples = "centered" -samples = "centered_fake" -samples = "distributed" +samples = "standard" +samples = "pole matching" algo = "rational" #algo = "RB" sampling = "quadrature" sampling = "quadrature_total" #sampling = "random" +samplingM = "quadrature" +#samplingM = "quadrature_total" +#samplingM = "random" + +if samples == "pole matching": + radialM = 0 + radialM = "gaussian" +# radialM = "thinplate" +# radialM = "multiquadric" + rMW0 = 2. + radialWeightM = [rMW0] if size == 1: # small mu0 = [4 ** .5, 1.5 ** .5] mutar = [5 ** .5, 1.75 ** .5] murange = [[2 ** .5, 1. ** .5], [6 ** .5, 2. ** .5]] elif size == 2: # medium mu0 = [4 ** .5, 1.75 ** .5] mutar = [5 ** .5, 1.25 ** .5] murange = [[1 ** .5, 1. ** .5], [7 ** .5, 2.5 ** .5]] elif size == 3: # fat mu0 = [6 ** .5, 4 ** .5] mutar = [2 ** .5, 2.5 ** .5] murange = [[0 ** .5, 2 ** .5], [12 ** .5, 6 ** .5]] elif size == 4: # crowded mu0 = [10 ** .5, 2 ** .5] mutar = [9 ** .5, 2.25 ** .5] murange = [[8 ** .5, 1.5 ** .5], [12 ** .5, 2.5 ** .5]] elif size == 5: # tall mu0 = [11 ** .5, 2.25 ** .5] mutar = [10.5 ** .5, 2.5 ** .5] murange = [[10 ** .5, 1.5 ** .5], [12 ** .5, 3 ** .5]] elif size == 6: # taller mu0 = [11 ** .5, 2.25 ** .5] mutar = [10.5 ** .5, 2.5 ** .5] murange = [[10 ** .5, 1.25 ** .5], [12 ** .5, 3.25 ** .5]] elif size == 7: # low mu0 = [7 ** .5, .75 ** .5] mutar = [6.5 ** .5, .9 ** .5] murange = [[6 ** .5, .5 ** .5], [8 ** .5, 1. ** .5]] -aEff = 1.25 +aEff = 1.1 bEff = 1. - aEff murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5, (aEff*murange[0][1]**2. + bEff*murange[1][1]**2.) ** .5], [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5, (aEff*murange[1][1]**2. + bEff*murange[0][1]**2.) ** .5]] solver = HSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 20, verbosity = verb) if ignore_forcing: solver.nbs = 1 -rescaling = [lambda x: np.power(x, 2.), lambda x: x] -rescalingInv = [lambda x: np.power(x, .5), lambda x: x] +rescalingExp = [2., 1.] if algo == "rational": params = {'N':MN, 'M':MN, 'S':S, 'POD':True} - if samples == "distributed": + if samples == "standard": params['polybasis'] = "CHEBYSHEV" # params['polybasis'] = "LEGENDRE" # params['polybasis'] = "MONOMIAL" params['E'] = MN method = RI - elif samples == "centered_fake": + elif samples == "centered": params['polybasis'] = "MONOMIAL" params['S'] = R method = RI - else: - params['S'] = R - method = RP + elif samples == "pole matching": + params['S'] = [SPivot[0]] + params['SMarginal'] = [SPivot[1]] + params['MMarginal'] = MMarginal + params['polybasisPivot'] = "CHEBYSHEV" + params['polybasisMarginal'] = "MONOMIAL" + params['matchingWeight'] = matchingWeight + params['radialBasisMarginal'] = radialM + params['radialBasisWeightsMarginal'] = radialWeightM + method = RIPM else: #if algo == "RB": params = {'R':R, 'S':S, 'POD':True} - if samples == "distributed": - method = RBD - elif samples == "centered_fake": - params['S'] = R - method = RBD - else: + if samples == "standard": + method = RB + elif samples == "centered": params['S'] = R - method = RBC + method = RB + elif samples == "pole matching": + params['S'] = [SPivot[0]] + params['SMarginal'] = [SPivot[1]] + params['MMarginal'] = MMarginal + params['polybasisMarginal'] = "MONOMIAL" + params['matchingWeight'] = matchingWeight + params['radialBasisMarginal'] = radialM + params['radialBasisWeightsMarginal'] = radialWeightM + method = RBPM -if samples == "distributed": +if samples == "standard": if sampling == "quadrature": - params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling, -# scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling, -# scalingInv = rescalingInv) + params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) params['S'] = [max(j, MN + 1) for j in params['S']] elif sampling == "quadrature_total": - params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) params['S'] = R else: # if sampling == "random": - params['sampler'] = RS(murange, "HALTON", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) params['S'] = R - -elif samples == "centered_fake": - params['sampler'] = MS(murange, points = [mu0], scaling = rescaling, - scalingInv = rescalingInv) +elif samples == "centered": + params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) +elif samples == "pole matching": + if sampling == "quadrature": + params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "CHEBYSHEV") +# params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "GAUSSLEGENDRE") +# params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "UNIFORM") + elif sampling == "quadrature_total": + params['samplerPivot'] = QST([murange[0][0], murange[1][0]], "CHEBYSHEV") + else: # if sampling == "random": + params['samplerPivot'] = RS([murange[0][0], murange[1][0]], "HALTON") + if samplingM == "quadrature": + params['samplerMarginal'] = QS([murange[0][1], murange[1][1]], "UNIFORM") + elif samplingM == "quadrature_total": + params['samplerMarginal'] = QST([murange[0][1], murange[1][1]], "CHEBYSHEV") + else: # if samplingM == "random": + params['samplerMarginal'] = RS([murange[0][1], murange[1][1]], "HALTON") -approx = method(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) +if samples != "pole matching": + approx = method(solver, mu0 = mu0, approxParameters = params, + verbosity = verb) +else: + approx = method(solver, mu0 = mu0, directionPivot = [0], + approxParameters = params, verbosity = verb) +if samples != "centered": approx.samplingEngine.allowRepeatedSamples = False approx.setupApprox() if show_sample: approx.plotApprox(mutar, name = 'u_app') approx.plotHF(mutar, name = 'u_HF') approx.plotErr(mutar, name = 'err') approx.plotRes(mutar, name = 'res') appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar) resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) if algo == "rational": from plot_zero_set import plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 1.]) if show_norm: solver._solveBatchSize = 100 from plot_inf_set import plotInfSet2 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( - murange, murangeEff, approx, mu0, 25, [2., 1.], clip = clip) + murange, murangeEff, approx, mu0, 20, [2., 1.], clip = clip, + relative = False, nobeta = True) diff --git a/examples/2d/pod/square_pod_hermite.py b/examples/2d/pod/square_pod_hermite.py index f512b92..6a6bd23 100644 --- a/examples/2d/pod/square_pod_hermite.py +++ b/examples/2d/pod/square_pod_hermite.py @@ -1,109 +1,95 @@ import numpy as np from rrompy.hfengines.linear_problem.bidimensional import \ HelmholtzSquareDomainProblemEngine as HSDPE -from rrompy.reduction_methods.centered import RationalPade as RP -from rrompy.reduction_methods.distributed import RationalInterpolant as RI -from rrompy.reduction_methods.centered import RBCentered as RBC -from rrompy.reduction_methods.distributed import RBDistributed as RBD +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, RandomSampler as RS, ManualSampler as MS) verb = 0 size = 1 show_sample = False show_norm = True MN = 4 R = (MN + 2) * (MN + 1) // 2 S0 = [3] * 2 S = [25] assert R < np.prod(S) -samples = "centered" -samples = "distributed" algo = "rational" #algo = "RB" sampling = "quadrature" sampling = "random" if size == 1: # small mu0 = [4 ** .5, 1.5 ** .5] mutar = [5 ** .5, 1.75 ** .5] murange = [[2 ** .5, 1. ** .5], [6 ** .5, 2. ** .5]] elif size == 2: # medium mu0 = [4 ** .5, 1.75 ** .5] mutar = [4.5 ** .5, 1.25 ** .5] murange = [[1 ** .5, 1. ** .5], [7 ** .5, 2.5 ** .5]] elif size == 3: # large mu0 = [6 ** .5, 4 ** .5] mutar = [2 ** .5, 2.5 ** .5] murange = [[0 ** .5, 2 ** .5], [12 ** .5, 6 ** .5]] elif size == 4: # crowded mu0 = [10 ** .5, 2 ** .5] mutar = [9 ** .5, 2.25 ** .5] murange = [[8 ** .5, 1.5 ** .5], [12 ** .5, 2.5 ** .5]] aEff = 1.25 bEff = 1. - aEff murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5, (aEff*murange[0][1]**2. + bEff*murange[1][1]**2.) ** .5], [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5, (aEff*murange[1][1]**2. + bEff*murange[0][1]**2.) ** .5]] solver = HSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 20, verbosity = verb) -rescaling = [lambda x: np.power(x, 2.), lambda x: x] -rescalingInv = [lambda x: np.power(x, .5), lambda x: x] +rescalingExp = [2., 1.] if algo == "rational": params = {'N':MN, 'M':MN, 'S':S, 'POD':True} - if samples == "distributed": - params['polybasis'] = "CHEBYSHEV" - method = RI - else: - method = RP + params['polybasis'] = "CHEBYSHEV" + method = RI else: #if algo == "RB": params = {'R':R, 'S':S, 'POD':True} - if samples == "distributed": - method = RBD - else: - method = RBC + method = RB -if samples == "distributed": - if sampling == "quadrature": - sampler0 = QS(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) - else: # if sampling == "random": - sampler0 = RS(murange, "SOBOL", scaling = rescaling, - scalingInv = rescalingInv) - S0 = np.prod(S0) +if sampling == "quadrature": + sampler0 = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) +else: # if sampling == "random": + sampler0 = RS(murange, "SOBOL", scalingExp = rescalingExp) + S0 = np.prod(S0) params['sampler'] = MS(murange, points = sampler0.generatePoints(S0)) approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) approx.setupApprox() if show_sample: approx.plotApprox(mutar, name = 'u_app') approx.plotHF(mutar, name = 'u_HF') approx.plotErr(mutar, name = 'err') approx.plotRes(mutar, name = 'res') appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar) resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) if algo == "rational": from plot_zero_set import plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 2.]) if show_norm: solver._solveBatchSize = 100 from plot_inf_set import plotInfSet2 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( murange, murangeEff, approx, mu0, 25, [2., 2.]) diff --git a/examples/2d/pod/square_simplified_pod.py b/examples/2d/pod/square_simplified_pod.py index 41ac4d1..8273c12 100644 --- a/examples/2d/pod/square_simplified_pod.py +++ b/examples/2d/pod/square_simplified_pod.py @@ -1,146 +1,127 @@ import numpy as np from rrompy.hfengines.linear_problem.bidimensional import \ HelmholtzSquareSimplifiedDomainProblemEngine as HSSDPE -from rrompy.reduction_methods.centered import RationalPade as RP -from rrompy.reduction_methods.distributed import RationalInterpolant as RI -from rrompy.reduction_methods.centered import RBCentered as RBC -from rrompy.reduction_methods.distributed import RBDistributed as RBD +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) verb = 5 size = 1 show_sample = False show_norm = True MN = 5 R = (MN + 2) * (MN + 1) // 2 S = [int(np.ceil(R ** .5))] * 2 samples = "centered" -samples = "centered_fake" -samples = "distributed" +samples = "standard" algo = "rational" #algo = "RB" sampling = "quadrature" sampling = "quadrature_total" sampling = "random" if size == 1: # small mu0 = [4 ** .5, 1.5 ** .5] mutar = [5 ** .5, 1.75 ** .5] murange = [[2 ** .5, 1. ** .5], [6 ** .5, 2. ** .5]] elif size == 2: # medium mu0 = [4 ** .5, 1.75 ** .5] mutar = [5 ** .5, 1.25 ** .5] murange = [[1 ** .5, 1. ** .5], [7 ** .5, 2.5 ** .5]] elif size == 3: # fat mu0 = [6 ** .5, 4 ** .5] mutar = [2 ** .5, 2.5 ** .5] murange = [[0 ** .5, 2 ** .5], [12 ** .5, 6 ** .5]] elif size == 4: # crowded mu0 = [10 ** .5, 2 ** .5] mutar = [9 ** .5, 2.25 ** .5] murange = [[8 ** .5, 1.5 ** .5], [12 ** .5, 2.5 ** .5]] elif size == 5: # tall mu0 = [11 ** .5, 2.25 ** .5] mutar = [10.5 ** .5, 2.5 ** .5] murange = [[10 ** .5, 1.5 ** .5], [12 ** .5, 3 ** .5]] elif size == 6: # taller mu0 = [11 ** .5, 2.25 ** .5] mutar = [10.5 ** .5, 2.5 ** .5] murange = [[10 ** .5, 1.25 ** .5], [12 ** .5, 3.25 ** .5]] elif size == 7: # low mu0 = [7 ** .5, .75 ** .5] mutar = [8 ** .5, 1 ** .5] murange = [[6 ** .5, .25 ** .5], [8 ** .5, 1.25 ** .5]] aEff = 1.25 bEff = 1. - aEff murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5, (aEff*murange[0][1]**2. + bEff*murange[1][1]**2.) ** .5], [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5, (aEff*murange[1][1]**2. + bEff*murange[0][1]**2.) ** .5]] solver = HSSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 20, verbosity = verb) -rescaling = [lambda x: np.power(x, 2.)] * 2 -rescalingInv = [lambda x: np.power(x, .5)] * 2 +rescalingExp = [2.] * 2 if algo == "rational": params = {'N':MN, 'M':MN, 'S':S, 'POD':True} - if samples == "distributed": + if samples == "standard": params['polybasis'] = "CHEBYSHEV" params['polybasis'] = "LEGENDRE" params['polybasis'] = "MONOMIAL" params['E'] = MN - method = RI - elif samples == "centered_fake": + elif samples == "centered": params['polybasis'] = "MONOMIAL" params['S'] = R - method = RI - else: - params['S'] = R - method = RP + method = RI else: #if algo == "RB": params = {'R':R, 'S':S, 'POD':True} - if samples == "distributed": - method = RBD - elif samples == "centered_fake": - params['S'] = R - method = RBD - else: + if samples == "centered": params['S'] = R - method = RBC + method = RB -if samples == "distributed": +if samples == "standard": if sampling == "quadrature": - params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling, -# scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling, -# scalingInv = rescalingInv) + params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) params['S'] = [max(j, MN + 1) for j in params['S']] elif sampling == "quadrature_total": - params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) params['S'] = R else: # if sampling == "random": - params['sampler'] = RS(murange, "HALTON", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) params['S'] = R -elif samples == "centered_fake": - params['sampler'] = MS(murange, points = [mu0], scaling = rescaling, - scalingInv = rescalingInv) +elif samples == "centered": + params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) approx.setupApprox() if show_sample: approx.plotApprox(mutar, name = 'u_app') approx.plotHF(mutar, name = 'u_HF') approx.plotErr(mutar, name = 'err') approx.plotRes(mutar, name = 'res') appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar) resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) if algo == "rational": from plot_zero_set import plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 2.]) if show_norm: solver._solveBatchSize = 100 from plot_inf_set import plotInfSet2 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( murange, murangeEff, approx, mu0, 25, [2., 2.]) diff --git a/examples/2d/pod/square_simplified_pod_hermite.py b/examples/2d/pod/square_simplified_pod_hermite.py index d7bf757..3257756 100644 --- a/examples/2d/pod/square_simplified_pod_hermite.py +++ b/examples/2d/pod/square_simplified_pod_hermite.py @@ -1,109 +1,95 @@ import numpy as np from rrompy.hfengines.linear_problem.bidimensional import \ HelmholtzSquareSimplifiedDomainProblemEngine as HSSDPE -from rrompy.reduction_methods.centered import RationalPade as RP -from rrompy.reduction_methods.distributed import RationalInterpolant as RI -from rrompy.reduction_methods.centered import RBCentered as RBC -from rrompy.reduction_methods.distributed import RBDistributed as RBD +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, RandomSampler as RS, ManualSampler as MS) verb = 0 size = 1 show_sample = False show_norm = True MN = 4 R = (MN + 2) * (MN + 1) // 2 S0 = [3] * 2 S = [25] assert R < np.prod(S) -samples = "centered" -samples = "distributed" algo = "rational" #algo = "RB" sampling = "quadrature" #sampling = "random" if size == 1: # small mu0 = [4 ** .5, 1.5 ** .5] mutar = [5 ** .5, 1.75 ** .5] murange = [[2 ** .5, 1. ** .5], [6 ** .5, 2. ** .5]] elif size == 2: # medium mu0 = [4 ** .5, 1.75 ** .5] mutar = [4.5 ** .5, 1.25 ** .5] murange = [[1 ** .5, 1. ** .5], [7 ** .5, 2.5 ** .5]] elif size == 3: # large mu0 = [6 ** .5, 4 ** .5] mutar = [2 ** .5, 2.5 ** .5] murange = [[0 ** .5, 2 ** .5], [12 ** .5, 6 ** .5]] elif size == 4: # crowded mu0 = [10 ** .5, 2 ** .5] mutar = [9 ** .5, 2.25 ** .5] murange = [[8 ** .5, 1.5 ** .5], [12 ** .5, 2.5 ** .5]] aEff = 1.25 bEff = 1. - aEff murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5, (aEff*murange[0][1]**2. + bEff*murange[1][1]**2.) ** .5], [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5, (aEff*murange[1][1]**2. + bEff*murange[0][1]**2.) ** .5]] solver = HSSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 20, verbosity = verb) -rescaling = [lambda x: np.power(x, 2.)] * 2 -rescalingInv = [lambda x: np.power(x, .5)] * 2 +rescalingExp = [2.] * 2 if algo == "rational": params = {'N':MN, 'M':MN, 'S':S, 'POD':True} - if samples == "distributed": - params['polybasis'] = "CHEBYSHEV" - method = RI - else: - method = RP + params['polybasis'] = "CHEBYSHEV" + method = RI else: #if algo == "RB": params = {'R':R, 'S':S, 'POD':True} - if samples == "distributed": - method = RBD - else: - method = RBC + method = RB -if samples == "distributed": - if sampling == "quadrature": - sampler0 = QS(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) - else: # if sampling == "random": - sampler0 = RS(murange, "SOBOL", scaling = rescaling, - scalingInv = rescalingInv) - S0 = np.prod(S0) +if sampling == "quadrature": + sampler0 = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) +else: # if sampling == "random": + sampler0 = RS(murange, "SOBOL", scalingExp = rescalingExp) + S0 = np.prod(S0) params['sampler'] = MS(murange, points = sampler0.generatePoints(S0)) approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) approx.setupApprox() if show_sample: approx.plotApprox(mutar, name = 'u_app') approx.plotHF(mutar, name = 'u_HF') approx.plotErr(mutar, name = 'err') approx.plotRes(mutar, name = 'res') appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar) resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) if algo == "rational": from plot_zero_set import plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 2.]) if show_norm: solver._solveBatchSize = 100 from plot_inf_set import plotInfSet2 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( murange, murangeEff, approx, mu0, 50, [2., 2.]) diff --git a/examples/2d/pod/synthetic_pod.py b/examples/2d/pod/synthetic_pod.py index a466a0d..b91fc84 100644 --- a/examples/2d/pod/synthetic_pod.py +++ b/examples/2d/pod/synthetic_pod.py @@ -1,158 +1,140 @@ import numpy as np from rrompy.hfengines.linear_problem.bidimensional import \ SyntheticBivariateEngine as SBE -from rrompy.reduction_methods.centered import RationalPade as RP -from rrompy.reduction_methods.distributed import RationalInterpolant as RI -from rrompy.reduction_methods.centered import RBCentered as RBC -from rrompy.reduction_methods.distributed import RBDistributed as RBD +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) verb = 5 size = 1 show_sample = True show_norm = True clip = -1 #clip = .4 #clip = .6 Delta = 0 MN = 10 R = (MN + 2) * (MN + 1) // 2 S = [int(np.ceil(R ** .5))] * 2 PODTol = 1e-6 samples = "centered" -samples = "centered_fake" -samples = "distributed" +samples = "standard" algo = "rational" #algo = "RB" sampling = "quadrature" #sampling = "quadrature_total" #sampling = "random" -if samples == "distributed": +if samples == "standard": radial = 0 radial = "gaussian" # radial = "thinplate" # radial = "multiquadric" rW0 = 10. radialWeight = [rW0] * 2 if size == 1: # small mu0 = [10. ** .5, 15. ** .5] mutar = [12. ** .5, 14. ** .5] murange = [[5. ** .5, 10. ** .5], [15 ** .5, 20 ** .5]] if size == 2: # large mu0 = [15. ** .5, 17.5 ** .5] mutar = [18. ** .5, 22. ** .5] murange = [[5. ** .5, 10. ** .5], [25 ** .5, 25 ** .5]] if size == 3: # medium mu0 = [17.5 ** .5, 15 ** .5] mutar = [20. ** .5, 18. ** .5] murange = [[10. ** .5, 10. ** .5], [25 ** .5, 20 ** .5]] assert Delta <= 0 aEff = 1.#25 bEff = 1. - aEff murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5, aEff*murange[0][1] + bEff*murange[1][1]], [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5, aEff*murange[1][1] + bEff*murange[0][1]]] kappa = 20. ** .5 theta = - np.pi / 6. n = 20 L = np.pi solver = SBE(kappa = kappa, theta = theta, n = n, L = L, mu0 = mu0, verbosity = verb) -rescaling = [lambda x: np.power(x, 2.)] * 2 -rescalingInv = [lambda x: np.power(x, .5)] * 2 +rescalingExp = [2.] * 2 if algo == "rational": params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True} - if samples == "distributed": + if samples == "standard": params['polybasis'] = "CHEBYSHEV" # params['polybasis'] = "LEGENDRE" # params['polybasis'] = "MONOMIAL" params['E'] = MN params['radialBasis'] = radial params['radialBasisWeights'] = radialWeight method = RI - elif samples == "centered_fake": + elif samples == "centered": params['polybasis'] = "MONOMIAL" params['S'] = R method = RI - else: - params['S'] = R - method = RP else: #if algo == "RB": params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':S, 'POD':True, 'PODTolerance':PODTol} - if samples == "distributed": - method = RBD - elif samples == "centered_fake": - params['S'] = R - method = RBD - else: + if samples == "centered": params['S'] = R - method = RBC + method = RB -if samples == "distributed": +if samples == "standard": if sampling == "quadrature": - params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling, -# scalingInv = rescalingInv) -# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling, -# scalingInv = rescalingInv) + params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) params['S'] = [max(j, MN + 1) for j in params['S']] elif sampling == "quadrature_total": - params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) params['S'] = R else: # if sampling == "random": - params['sampler'] = RS(murange, "HALTON", scaling = rescaling, - scalingInv = rescalingInv) + params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) params['S'] = R -elif samples == "centered_fake": - params['sampler'] = MS(murange, points = [mu0], scaling = rescaling, - scalingInv = rescalingInv) +elif samples == "centered": + params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) -if samples == "distributed": approx.samplingEngine.allowRepeatedSamples = False +if samples == "standard": approx.samplingEngine.allowRepeatedSamples = False approx.setupApprox() if show_sample: L = mutar[1] approx.plotApprox(mutar, name = 'u_app', homogeneized = False, what = "REAL") approx.plotHF(mutar, name = 'u_HF', homogeneized = False, what = "REAL") approx.plotErr(mutar, name = 'err', homogeneized = False, what = "REAL") # approx.plotRes(mutar, name = 'res', homogeneized = False, what = "REAL") appErr = approx.normErr(mutar) solNorm = approx.normHF(mutar) resNorm = approx.normRes(mutar) RHSNorm = approx.normRHS(mutar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) if algo == "rational" and approx.N > 0: from plot_zero_set import plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 2.], clip = clip) if show_norm: solver._solveBatchSize = 100 from plot_inf_set import plotInfSet2 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( murange, murangeEff, approx, mu0, 50, [2., 2.], clip = clip, relative = False) diff --git a/examples/3d/base/fracture3.py b/examples/3d/base/fracture3.py new file mode 100644 index 0000000..02f4a18 --- /dev/null +++ b/examples/3d/base/fracture3.py @@ -0,0 +1,35 @@ +from rrompy.hfengines.linear_problem.tridimensional import \ + MembraneFractureEngine3 as MFE +from fracture3_warping import fracture3_warping + +verb = 100 + +mu0 = [45. ** .5, .7, .075] + +H = 1. +L = .75 +delta = .05 +n = 50 + +solver = MFE(mu0 = mu0, H = H, L = L, delta = delta, + n = n, verbosity = verb) +u0 = solver.liftDirichletData(mu0) +uh = solver.solve(mu0)[0] +#solver.plotmesh(figsize = (7.5, 4.5)) +#solver.plot(u0, what = 'REAL', figsize = (8, 5)) +print(solver.norm(uh)) +#solver.plot(uh, what = 'REAL', figsize = (8, 5)) +#solver.plot(solver.residual(uh, mu0)[0], name = 'res', +# what = 'REAL', figsize = (8, 5)) +#solver.outParaviewTimeDomain(uh, mu0[0], filename = 'out', folder = True, +# forceNewFile = False) + +warps = fracture3_warping(solver.V.mesh(), L, mu0[1], delta, mu0[2]) + +#solver.plotmesh(warps, figsize = (7.5, 4.5)) +#solver.plot(u0, warps, what = 'REAL', figsize = (8, 5)) +solver.plot(uh, warps, what = 'REAL', figsize = (8, 5)) +#solver.plot(solver.residual(uh, mu0)[0], warps, name = 'res', +# what = 'REAL', figsize = (8, 5)) +#solver.outParaviewTimeDomain(uh, mu0[0], warps, filename = 'outW', +# folder = True, forceNewFile = False) diff --git a/examples/3d/base/fracture3_warping.py b/examples/3d/base/fracture3_warping.py new file mode 100644 index 0000000..6278be1 --- /dev/null +++ b/examples/3d/base/fracture3_warping.py @@ -0,0 +1,38 @@ +import numpy as np +import ufl +import fenics as fen +from rrompy.solver.fenics import affine_warping + +def fracture3_warping(mesh0, L, nu, delta0, delta): + shiftWarp = (delta0 - delta) / 4. / L * (2. * L + delta0) + x, y = fen.SpatialCoordinate(mesh0) + warpUL, warpIUL = affine_warping(mesh0, + np.array([[1. + (delta0 - delta) / 2. / L, 0], + [0, 2. * nu]]), + np.array([shiftWarp, 0.])) + warpUR, warpIUR = affine_warping(mesh0, + np.array([[1. + (delta0 - delta) / 2. / L, 0], + [0, 2. * nu]]), + np.array([- shiftWarp, 0.])) + warpDL, warpIDL = affine_warping(mesh0, + np.array([[1. + (delta0 - delta) / 2. / L, 0], + [0, 2. - 2. * nu]]), + np.array([shiftWarp, 0.])) + warpDR, warpIDR = affine_warping(mesh0, + np.array([[1. + (delta0 - delta) / 2. / L, 0], + [0, 2. - 2. * nu]]), + np.array([- shiftWarp, 0.])) + warpDC, warpIDC = affine_warping(mesh0, np.array([[delta / delta0, 0], + [0, 2. - 2. * nu]])) + + warp = ufl.conditional(ufl.gt(y, 0.), + ufl.conditional(ufl.gt(x, 0.), warpUR, warpUL), + ufl.conditional(ufl.gt(x, .5 * delta0), warpDR, + ufl.conditional(ufl.lt(x, - .5 * delta0), + warpDL, warpDC))) + warpI = ufl.conditional(ufl.gt(y, 0.), + ufl.conditional(ufl.gt(x, 0.), warpIUR, warpIUL), + ufl.conditional(ufl.gt(x, .5 * delta), warpIDR, + ufl.conditional(ufl.lt(x, - .5 * delta), + warpIDL, warpIDC))) + return warp, warpI \ No newline at end of file diff --git a/examples/3d/base/matrix_3.py b/examples/3d/base/matrix_3.py new file mode 100644 index 0000000..dce8d7b --- /dev/null +++ b/examples/3d/base/matrix_3.py @@ -0,0 +1,74 @@ +import scipy.sparse as sp +import numpy as np +from rrompy.hfengines.base import MatrixEngineBase as MEB + +verb = 100 + +deg = 2 + +N = 150 +exp = 1.05 +assert exp > 1. +empty = sp.csr_matrix((np.zeros(0), np.zeros(0), np.zeros(N + 1)), + shape = (N, N)) +solver = MEB(verbosity = verb) +solver.npar = 3 +A0 = sp.spdiags([np.arange(1, 1 + 2 * N, 2) ** exp - (2 * (N // 4)) ** exp], + [0], N, N) +if deg == 1: + solver.nAs = 4 + solver.As = [A0, sp.eye(N), - sp.eye(N), - sp.eye(N)] +elif deg == 2: + solver.nAs = 7 + solver.As = ([A0] + [empty] + [- sp.eye(N)] + [empty] * 3 + [sp.eye(N)]) +elif deg == 3: + solver.nAs = 13 + solver.As = ([A0] + [empty] + [- sp.eye(N)] + [empty] * 9 + [sp.eye(N)]) +np.random.seed(420) +solver.nbs = 1 +solver.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] + +mu0 = [.8, 0., .2] + +uh = solver.solve(mu0)[0] +solver.plot(uh, what = 'REAL', figsize = (8, 5)) + + + +murange = np.linspace(-1., 1., 20) +Mu1, Mu2, Mu3 = np.meshgrid(murange, murange, murange, indexing = 'ij') +mus = [] +if deg == 1: + f = Mu1 - Mu2 - Mu3 +elif deg == 2: + f = Mu1 * Mu3 - Mu2 +elif deg == 3: + f = Mu3 * Mu1**2 - Mu2 + +from matplotlib import pyplot as plt +pts = [] +for jdir in range(len(Mu3)): + z = Mu3[0, 0, jdir] + xis, yis, fis = Mu1[:, :, jdir], Mu2[:, :, jdir], f[:, :, jdir] + plt.figure() + CS = plt.contour(xis, yis, np.real(fis), levels = [0.]) + plt.close() + for i, CSc in enumerate(CS.allsegs): + if np.isclose(CS.cvalues[i], 0.): + for j, CScj in enumerate(CSc): + for k in range(len(CScj)): + pts += [[CScj[k, 0], CScj[k, 1], z]] + pts += [[np.nan] * 3] +pts = np.array(pts) +from mpl_toolkits.mplot3d import Axes3D +fig = plt.figure(figsize = (8, 6)) +ax = Axes3D(fig) +ax.plot(pts[:, 0], pts[:, 1], pts[:, 2], 'k-') +ax.set_xlim(-1., 1.) +ax.set_ylim(-1., 1.) +ax.set_zlim(-1., 1.) +ax.set_xlabel('x') +ax.set_ylabel('y') +ax.set_zlabel('z') +plt.show() +plt.close() diff --git a/examples/3d/base/scattering1.py b/examples/3d/base/scattering1.py new file mode 100644 index 0000000..0124267 --- /dev/null +++ b/examples/3d/base/scattering1.py @@ -0,0 +1,29 @@ +from rrompy.hfengines.linear_problem.tridimensional import Scattering1d as S1D +import fenics as fen +import numpy as np + +verb = 100 + +mu0 = [4., np.pi, 0.] +mu = [4.5, np.pi, 1.] + +n = 100 + +solver = S1D(mu0 = mu0, n = n, verbosity = verb) +u0 = solver.liftDirichletData(mu0) +uh = solver.solve(mu)[0] +#solver.plotmesh(figsize = (12, 2)) +#solver.plot(u0, what = ['REAL', 'IMAG'], figsize = (12, 4)) +print(solver.norm(uh)) +#solver.plot(uh, what = ['REAL', 'IMAG'], figsize = (12, 4)) +#solver.plot(solver.residual(uh, mu0)[0], name = 'res', +# what = ['REAL', 'IMAG'], figsize = (12, 4)) + +x = fen.SpatialCoordinate(solver.V.mesh()) +warps = [x * mu[1] / solver._L - x, x * solver._L / mu[1] - x] + +#solver.plotmesh(warps, figsize = (12, 2)) +#solver.plot(u0, warps, what = ['REAL', 'IMAG'], figsize = (12, 4)) +solver.plot(uh, warps, what = ['REAL', 'IMAG'], figsize = (12, 4)) +#solver.plot(solver.residual(uh, mu0)[0], warps, name = 'res', +# what = ['REAL', 'IMAG'], figsize = (12, 4)) diff --git a/examples/3d/pod/fracture3_pod.py b/examples/3d/pod/fracture3_pod.py new file mode 100644 index 0000000..802dca1 --- /dev/null +++ b/examples/3d/pod/fracture3_pod.py @@ -0,0 +1,253 @@ +import numpy as np +from mpl_toolkits.mplot3d import Axes3D +from matplotlib import pyplot as plt +from rrompy.hfengines.linear_problem.tridimensional import \ + MembraneFractureEngine3 as MFE +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB +from rrompy.reduction_methods.pole_matching import \ + RationalInterpolantPoleMatching as RIPM +from rrompy.reduction_methods.pole_matching import \ + ReducedBasisPoleMatching as RBPM +from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, + QuadratureSamplerTotal as QST, + ManualSampler as MS, + RandomSampler as RS) + +verb = 70 +size = 4 +show_sample = False +show_norm = False +clip = -1 +#clip = .4 +#clip = .6 + +Delta = 0 +MN = 5 +R = (MN + 3) * (MN + 2) * (MN + 1) // 6 +S = [int(np.ceil(R ** (1. / 3.)))] * 3 +PODTol = 1e-8 +SPivot = [MN + 1, 2, 2] +MMarginal = SPivot[1] - 1 +matchingWeight = 10. + +samples = "centered" +samples = "standard" +samples = "pole matching" +algo = "rational" +#algo = "RB" +sampling = "quadrature" +sampling = "quadrature_total" +#sampling = "random" +samplingM = "quadrature" +#samplingM = "quadrature_total" +#samplingM = "random" + +if samples == "standard": + radial = 0 + radial = "gaussian" +# radial = "thinplate" +# radial = "multiquadric" + rW0 = 5. + radialWeight = [rW0] * 3 +if samples == "pole matching": + radial = 0 +# radial = "gaussian" +# radial = "thinplate" +# radial = "multiquadric" + rW0 = 5. + radialWeight = [rW0] + radialM = 0 + radialM = "gaussian" +# radialM = "thinplate" +# radialM = "multiquadric" + rMW0 = 2. + radialWeightM = [rMW0] * 2 + +assert Delta <= 0 + +if size == 1: + mu0 = [47.5 ** .5, .4, .05] + mutar = [50 ** .5, .45, .07] + murange = [[40 ** .5, .3, .025], [55 ** .5, .5, .075]] +if size == 2: + mu0 = [50 ** .5, .3, .02] + mutar = [55 ** .5, .4, .03] + murange = [[40 ** .5, .1, .005], [60 ** .5, .5, .035]] +if size == 3: + mu0 = [45 ** .5, .5, .05] + mutar = [47 ** .5, .4, .045] + murange = [[40 ** .5, .3, .04], [50 ** .5, .7, .06]] +if size == 4: + mu0 = [45 ** .5, .4, .05] + mutar = [47 ** .5, .45, .045] + murange = [[40 ** .5, .3, .04], [50 ** .5, .5, .06]] + +aEff = 1.#25 +bEff = 1. - aEff +murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5, + aEff*murange[0][1] + bEff*murange[1][1], + aEff*murange[0][2] + bEff*murange[1][2]], + [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5, + aEff*murange[1][1] + bEff*murange[0][1], + aEff*murange[1][2] + bEff*murange[0][2]]] + +H = 1. +L = .75 +delta = .05 +n = 50 + +solver = MFE(mu0 = mu0, H = H, L = L, delta = delta, n = n, verbosity = verb) + +rescalingExp = [2., 1., 1.] +if algo == "rational": + params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True} + if samples == "standard": + params['polybasis'] = "CHEBYSHEV" +# params['polybasis'] = "LEGENDRE" +# params['polybasis'] = "MONOMIAL" + params['E'] = MN + params['radialBasis'] = radial + params['radialBasisWeights'] = radialWeight + method = RI + elif samples == "centered": + params['polybasis'] = "MONOMIAL" + params['S'] = R + method = RI + elif samples == "pole matching": + params['S'] = [SPivot[0]] + params['SMarginal'] = SPivot[1:] + params['MMarginal'] = MMarginal + params['polybasisPivot'] = "CHEBYSHEV" + params['polybasisMarginal'] = "MONOMIAL" + params['matchingWeight'] = matchingWeight + params['radialBasisPivot'] = radial + params['radialBasisMarginal'] = radialM + params['radialBasisWeightsPivot'] = radialWeight + params['radialBasisWeightsMarginal'] = radialWeightM + method = RIPM +else: #if algo == "RB": + params = {'R':(MN + 3 + Delta) * (MN + 2 + Delta) * (MN + 1 + Delta) // 6, + 'S':S, 'POD':True, 'PODTolerance':PODTol} + if samples == "standard": + method = RB + elif samples == "centered": + params['S'] = R + method = RB + elif samples == "pole matching": + params['S'] = [SPivot[0]] + params['SMarginal'] = SPivot[1:] + params['MMarginal'] = MMarginal + params['polybasisMarginal'] = "MONOMIAL" + params['matchingWeight'] = matchingWeight + params['radialBasisMarginal'] = radialM + params['radialBasisWeightsMarginal'] = radialWeightM + method = RBPM + +if samples == "standard": + if sampling == "quadrature": + params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "GAUSSLEGENDRE",scalingExp = rescalingExp) +# params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) + params['S'] = [max(j, MN + 1) for j in params['S']] + elif sampling == "quadrature_total": + params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) + params['S'] = R + else: # if sampling == "random": + params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) + params['S'] = R + +elif samples == "centered": + params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) +elif samples == "pole matching": + if sampling == "quadrature": + params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "CHEBYSHEV") +# params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "GAUSSLEGENDRE") +# params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "UNIFORM") + elif sampling == "quadrature_total": + params['samplerPivot'] = QST([murange[0][0], murange[1][0]], "CHEBYSHEV") + params['S'] = MN + 1 + else: # if sampling == "random": + params['samplerPivot'] = RS([murange[0][0], murange[1][0]], "HALTON") + params['S'] = MN + 1 + if samplingM == "quadrature": + params['samplerMarginal'] = QS([murange[0][1:], murange[1][1:]], "UNIFORM") + elif samplingM == "quadrature_total": + params['samplerMarginal'] = QST([murange[0][1:], murange[1][1:]], "CHEBYSHEV") + params['SMarginal'] = (MMarginal + 2) * (MMarginal + 1) // 2 + params['polybasisMarginal'] = "CHEBYSHEV" + else: # if samplingM == "random": + params['samplerMarginal'] = RS([murange[0][1:], murange[1][1:]], "HALTON") + params['SMarginal'] = (MMarginal + 2) * (MMarginal + 1) // 2 + +if samples != "pole matching": + approx = method(solver, mu0 = mu0, approxParameters = params, + verbosity = verb) +else: + approx = method(solver, mu0 = mu0, directionPivot = [0], + approxParameters = params, verbosity = verb) +if samples != "centered": approx.samplingEngine.allowRepeatedSamples = False + +approx.setupApprox() +if show_sample: + from fracture3_warping import fracture3_warping + warps = fracture3_warping(solver.V.mesh(), L, mutar[1], delta, mutar[2]) + approx.plotApprox(mutar, warps, name = 'u_app', + homogeneized = False, what = "REAL") + approx.plotHF(mutar, warps, name = 'u_HF', + homogeneized = False, what = "REAL") + approx.plotErr(mutar, warps, name = 'err', + homogeneized = False, what = "REAL") +# approx.plotRes(mutar, warps, name = 'res', +# homogeneized = False, what = "REAL") + appErr = approx.normErr(mutar) + solNorm = approx.normHF(mutar) + resNorm = approx.normRes(mutar) + RHSNorm = approx.normRHS(mutar) + print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, + np.divide(appErr, solNorm))) + print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, + np.divide(resNorm, RHSNorm))) + +fig = plt.figure(figsize = (8, 6)) +ax = Axes3D(fig) +ax.scatter(np.real(approx.mus(0) ** 2.), np.real(approx.mus(1)), + np.real(approx.mus(2)), '.') +plt.show() +plt.close() + +approx.verbosity = 0 +approx.trainedModel.verbosity = 0 +from plot_zero_set_3 import plotZeroSet3 +#muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, +# [None, mu0[1], mu0[2]], [2., 1., 1.], +# clip = clip) +#muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, +# [None, None, mu0[2]], [2., 1., 1.], +# clip = clip) +#muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, +# [None, mu0[1], None], [2., 1., 1.], +# clip = clip) +muZeroScatter = plotZeroSet3(murange, murangeEff, approx, mu0, 50, + [None, None, None], [2., 1., 1.], clip = clip) + +if show_norm: + solver._solveBatchSize = 25 + from plot_inf_set_3 import plotInfSet3 + muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( + murange, murangeEff, approx, mu0, 25, + [None, mu0[1], mu0[2]], [2., 1., 1.], + clip = clip, relative = False, + normalizeDen = True) + muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( + murange, murangeEff, approx, mu0, 25, + [None, None, mu0[2]], [2., 1., 1.], + clip = clip, relative = False, + normalizeDen = True) + muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( + murange, murangeEff, approx, mu0, 25, + [None, mu0[1], None], [2., 1., 1.], + clip = clip, relative = False, + normalizeDen = True) + +print(approx.getPoles([None, .5, .05]) ** 2.) diff --git a/examples/3d/pod/fracture3_warping.py b/examples/3d/pod/fracture3_warping.py new file mode 120000 index 0000000..71cd0c4 --- /dev/null +++ b/examples/3d/pod/fracture3_warping.py @@ -0,0 +1 @@ +/home/pradovera/Repos/RROMPy/examples/3d/base/fracture3_warping.py \ No newline at end of file diff --git a/examples/3d/pod/matrix_3_pod.py b/examples/3d/pod/matrix_3_pod.py new file mode 100644 index 0000000..ae358a5 --- /dev/null +++ b/examples/3d/pod/matrix_3_pod.py @@ -0,0 +1,187 @@ +import numpy as np +import scipy.sparse as sp +from mpl_toolkits.mplot3d import Axes3D +from matplotlib import pyplot as plt +from rrompy.hfengines.base import MatrixEngineBase as MEB +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB +from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, + QuadratureSamplerTotal as QST, + ManualSampler as MS, + RandomSampler as RS) + +verb = 50 +deg = 2 +size = 1 +show_sample = True +show_norm = False + +Delta = 0 +MN = 15 +R = (MN + 3) * (MN + 2) * (MN + 1) // 6 +S = [int(np.ceil(R ** (1. / 3.)))] * 3 +PODTol = 1e-8 + +samples = "centered" +samples = "standard" +#samples, nDer = "standard_MMM", 2 +algo = "rational" +#algo = "RB" +sampling = "quadrature" +sampling = "quadrature_total" +sampling = "random" + +if samples == "standard": + radial = 0 +# radial = "gaussian" +# radial = "thinplate" +# radial = "multiquadric" + rW0 = 5. + radialWeight = [rW0] * 2 + +assert Delta <= 0 + +if size == 1: + mu0 = [0.] * 3 + mutar = [.8, .8, .8] + murange = [[-1.] * 3, [1.] * 3] + +aEff = 1.#25 +bEff = 1. - aEff +murangeEff = [[aEff*murange[0][0] + bEff*murange[1][0], + aEff*murange[0][1] + bEff*murange[1][1], + aEff*murange[0][2] + bEff*murange[1][2]], + [aEff*murange[1][0] + bEff*murange[0][0], + aEff*murange[1][1] + bEff*murange[0][1], + aEff*murange[1][2] + bEff*murange[0][2]]] + +N = 150 +exp = 1.05 +assert exp > 1. +empty = sp.csr_matrix((np.zeros(0), np.zeros(0), np.zeros(N + 1)), + shape = (N, N)) +solver = MEB(verbosity = verb) +solver.npar = 3 +A0 = sp.spdiags([np.arange(1, 1 + 2 * N, 2) ** exp - (2 * (N // 4)) ** exp], + [0], N, N) +if deg == 1: + solver.nAs = 4 + solver.As = [A0, sp.eye(N), - sp.eye(N), - sp.eye(N)] +elif deg == 2: + solver.nAs = 7 + solver.As = [A0] + [empty] + [- sp.eye(N)] + [empty] * 3 + [sp.eye(N)] +elif deg == 3: + solver.nAs = 13 + solver.As = [A0] + [empty] + [- sp.eye(N)] + [empty] * 9 + [sp.eye(N)] +np.random.seed(420) +solver.nbs = 1 +solver.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] + + +if algo == "rational": + params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True} + if samples == "standard": + params['polybasis'] = "CHEBYSHEV" +# params['polybasis'] = "LEGENDRE" +# params['polybasis'] = "MONOMIAL" + params['E'] = MN + params['radialBasis'] = radial + params['radialBasisWeights'] = radialWeight + elif samples == "centered": + params['polybasis'] = "MONOMIAL" + params['S'] = R + else: #MMM + params['S'] = nDer * int(np.ceil(R / nDer)) + method = RI +else: #if algo == "RB": + params = {'R':(MN + 3 + Delta) * (MN + 2 + Delta) * (MN + 1 + Delta) // 6, + 'S':S, 'POD':True, 'PODTolerance':PODTol} + if samples == "standard": + pass + elif samples == "centered": + params['S'] = R + else: #MMM + params['S'] = nDer * int(np.ceil(R / nDer)) + method = RB + +if samples == "standard": + if sampling == "quadrature": + params['sampler'] = QS(murange, "CHEBYSHEV") +# params['sampler'] = QS(murange, "GAUSSLEGENDRE") +# params['sampler'] = QS(murange, "UNIFORM") + params['S'] = [max(j, MN + 1) for j in params['S']] + elif sampling == "quadrature_total": + params['sampler'] = QST(murange, "CHEBYSHEV") + params['S'] = R + else: # if sampling == "random": + params['sampler'] = RS(murange, "HALTON") + params['S'] = R +elif samples == "centered": + params['sampler'] = MS(murange, points = [mu0]) +elif samples == "standard_MMM": + if sampling == "quadrature": + SdirEff = int(np.ceil(int(np.ceil(R / nDer)) ** (1. / 3))) + pts = QS(murange, "CHEBYSHEV").generatePoints([SdirEff] * 3) + elif sampling == "quadrature_total": + pts = QST(murange, "CHEBYSHEV").generatePoints(int(np.ceil(R / nDer))) + else: # if sampling == "random": + pts = RS(murange, "HALTON").generatePoints(int(np.ceil(R / nDer))) + params['sampler'] = MS(murange, points = pts) + +approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) +if samples == "standard": approx.samplingEngine.allowRepeatedSamples = False + +approx.setupApprox() +if show_sample: + approx.plotApprox(mutar, name = 'u_app', what = "REAL") + approx.plotHF(mutar, name = 'u_HF', what = "REAL") + approx.plotErr(mutar, name = 'err', what = "REAL") +# approx.plotRes(mutar, name = 'res', what = "REAL") + appErr = approx.normErr(mutar) + solNorm = approx.normHF(mutar) + resNorm = approx.normRes(mutar) + RHSNorm = approx.normRHS(mutar) + print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, + np.divide(appErr, solNorm))) + print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, + np.divide(resNorm, RHSNorm))) + +fig = plt.figure(figsize = (8, 6)) +ax = Axes3D(fig) +ax.scatter(approx.trainedModel.data.mus(0), approx.trainedModel.data.mus(1), + approx.trainedModel.data.mus(2), '.') +ax.set_xlim3d(murangeEff[0][0], murangeEff[1][0]) +ax.set_ylim3d(murangeEff[0][1], murangeEff[1][1]) +ax.set_zlim3d(murangeEff[0][2], murangeEff[1][2]) +plt.show() +plt.close() + +approx.verbosity = 0 +approx.trainedModel.verbosity = 0 +if algo == "rational" and approx.N > 0: + from plot_zero_set_3 import plotZeroSet3 +# muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, +# [None, mu0[1], mu0[2]], [1., 1., 1.]) +# muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, +# [None, None, mu0[2]], [1., 1., 1.]) +# muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, +# [None, mu0[1], None], [1., 1., 1.]) + plotZeroSet3(murange, murangeEff, approx, mu0, 25, [None, None, None], + [1., 1., 1.], imagTol = 1e-2) + +if show_norm: + solver._solveBatchSize = 25 + from plot_inf_set_3 import plotInfSet3 + muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( + murange, murangeEff, approx, mu0, 25, + [None, mu0[1], mu0[2]], [1., 1., 1.], + relative = False, normalizeDen = True) + muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( + murange, murangeEff, approx, mu0, 25, + [None, None, mu0[2]], [1., 1., 1.], + relative = False, normalizeDen = True) + muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( + murange, murangeEff, approx, mu0, 25, + [None, mu0[1], None], [1., 1., 1.], + relative = False, normalizeDen = True) + diff --git a/examples/3d/pod/plot_inf_set.py b/examples/3d/pod/plot_inf_set.py new file mode 120000 index 0000000..ad92d4d --- /dev/null +++ b/examples/3d/pod/plot_inf_set.py @@ -0,0 +1 @@ +/home/pradovera/Repos/RROMPy/examples/2d/pod/plot_inf_set.py \ No newline at end of file diff --git a/examples/3d/pod/plot_inf_set_3.py b/examples/3d/pod/plot_inf_set_3.py new file mode 100644 index 0000000..4ca96db --- /dev/null +++ b/examples/3d/pod/plot_inf_set_3.py @@ -0,0 +1,359 @@ +import warnings +from copy import deepcopy as copy +import numpy as np +from matplotlib import pyplot as plt + +def plotInfSet31FromData(mNone, mus, Z, T, R, E, beta, murange, approx, mu0, + exp = 2., normalizeDen = False): + if hasattr(approx, "mus"): + mu2x = approx.mus(mNone[0]) ** exp + else: + mu2x = mu0[0] ** exp + murangeExp = [[murange[0][mNone[0]] ** exp], + [murange[1][mNone[0]] ** exp]] + mu1 = np.real(np.power([m[mNone[0]] for m in mus], exp)) + ZTmin, ZTmax = min(np.min(Z), np.min(T)), max(np.max(Z), np.max(T)) + Rmin, Rmax = np.min(R), np.max(R) + Emin, Emax = np.min(E), np.max(E) + if not np.isnan(beta[0]): + eta = R / beta / E + betamin, betamax = np.min(beta), np.max(beta) + etamin, etamax = np.min(eta), np.max(eta) + plt.figure(figsize = (15, 7)) + plt.jet() + plt.semilogy(mu1, Z) + plt.semilogy(mu1, T, '--') + mTMP = mus[0] + mTMP[mNone[0]] = None + for l_ in approx.trainedModel.getPoles(mTMP): + plt.plot([np.real(l_ ** exp)] * 2, [ZTmin, ZTmax], 'b:') + plt.plot(mu2x, [ZTmin] * len(mu2x), 'kx') + plt.plot([murangeExp[0][0]] * 2, [ZTmin, ZTmax], 'm:') + plt.plot([murangeExp[1][0]] * 2, [ZTmin, ZTmax], 'm:') + plt.xlim(mu1[0], mu1[-1]) + plt.title("|u(mu)|, |u_app(mu)|") + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + plt.semilogy(mu1, R) + for l_ in approx.trainedModel.getPoles(mTMP): + plt.plot([np.real(l_ ** exp)] * 2, [Rmin, Rmax], 'b:') + plt.plot(mu2x, [Rmax] * len(mu2x), 'kx') + plt.plot([murangeExp[0][0]] * 2, [Rmin, Rmax], 'm:') + plt.plot([murangeExp[1][0]] * 2, [Rmin, Rmax], 'm:') + plt.xlim(mu1[0], mu1[-1]) + if normalizeDen: + plt.title("|Q(mu)res(mu)|") + else: + plt.title("|res(mu)|") + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + plt.semilogy(mu1, E) + for l_ in approx.trainedModel.getPoles(mTMP): + plt.plot([np.real(l_ ** exp)] * 2, [Emin, Emax], 'b:') + plt.plot(mu2x, [Emax] * len(mu2x), 'kx') + plt.plot([murangeExp[0][0]] * 2, [Emin, Emax], 'm:') + plt.plot([murangeExp[1][0]] * 2, [Emin, Emax], 'm:') + plt.xlim(mu1[0], mu1[-1]) + if normalizeDen: + plt.title("|Q(mu)err(mu)|") + else: + plt.title("|err(mu)|") + plt.grid() + plt.show() + + if not np.isnan(beta[0]): + plt.figure(figsize = (15, 7)) + plt.jet() + plt.plot(mu1, beta) + for l_ in approx.trainedModel.getPoles(mTMP): + plt.plot([np.real(l_ ** exp)] * 2, [betamin, betamax], 'b:') + plt.plot(mu2x, [betamax] * len(mu2x), 'kx') + plt.plot([murangeExp[0][0]] * 2, [betamin, betamax], 'm:') + plt.plot([murangeExp[1][0]] * 2, [betamin, betamax], 'm:') + plt.xlim(mu1[0], mu1[-1]) + plt.title("beta(mu)") + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + plt.semilogy(mu1, R / beta) + plt.semilogy(mu1, E, '--') + for l_ in approx.trainedModel.getPoles(mTMP): + plt.plot([np.real(l_ ** exp)] * 2, [Emin, Emax], 'b:') + plt.plot(mu2x, [Emax] * len(mu2x), 'kx') + plt.plot([murangeExp[0][0]] * 2, [Emin, Emax], 'm:') + plt.plot([murangeExp[1][0]] * 2, [Emin, Emax], 'm:') + plt.xlim(mu1[0], mu1[-1]) + if normalizeDen: + plt.title("|Q(mu)res(mu)/beta(mu)|") + else: + plt.title("|res(mu)/beta(mu)|") + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + plt.semilogy(mu1, eta) + for l_ in approx.trainedModel.getPoles(mTMP): + plt.plot([np.real(l_ ** exp)] * 2, [etamin, etamax], 'b:') + plt.plot(mu2x, [etamax] * len(mu2x), 'kx') + plt.plot([murangeExp[0][0]] * 2, [etamin, etamax], 'm:') + plt.plot([murangeExp[1][0]] * 2, [etamin, etamax], 'm:') + plt.xlim(mu1[0], mu1[-1]) + plt.title("eta(mu)") + plt.grid() + plt.show() + +def plotInfSet32FromData(mNone, mus, Ze, Te, Re, Ee, beta, murange, approx, + mu0, exps = [2., 1.], clip = -1, + normalizeDen = False): + if hasattr(approx, "mus"): + mu2x = approx.mus(mNone[0]) ** exps[0] + mu2y = approx.mus(mNone[1]) ** exps[1] + else: + mu2x, mu2y = mu0[mNone[0]] ** exps[0], mu0[mNone[1]] ** exps[1] + murangeExp = [[murange[0][mNone[0]] ** exps[0], + murange[0][mNone[1]] ** exps[1]], + [murange[1][mNone[0]] ** exps[0], + murange[1][mNone[1]] ** exps[1]]] + mu1s = np.unique([m[mNone[0]] for m in mus]) + mu2s = np.unique([m[mNone[1]] for m in mus]) + mu1 = np.power(mu1s, exps[0]) + mu2 = np.power(mu2s, exps[1]) + Mu1, Mu2 = np.meshgrid(np.real(mu1), np.real(mu2)) + + Z = np.log10(Ze) + T = np.log10(Te) + R = np.log10(Re) + E = np.log10(Ee) + ZTmin, ZTmax = min(np.min(Z), np.min(T)), max(np.max(Z), np.max(T)) + Rmin, Rmax = np.min(R), np.max(R) + Emin, Emax = np.min(E), np.max(E) + if not np.isnan(beta[0, 0]): + betamin, betamax = np.min(beta), np.max(beta) + if clip > 0: + ZTmax -= clip * (ZTmax - ZTmin) + cmap = plt.cm.bone + else: + cmap = plt.cm.jet + warnings.simplefilter("ignore", category = UserWarning) + plt.figure(figsize = (15, 7)) + plt.jet() + p = plt.contourf(Mu1, Mu2, Z, cmap = cmap, + levels = np.linspace(ZTmin, ZTmax, 50)) + if clip > 0: + plt.contour(Mu1, Mu2, Z, [ZTmin]) + plt.plot(mu2x, mu2y, 'kx') + plt.plot([murangeExp[0][0]] * 2, + [murangeExp[0][1], murangeExp[1][1]], 'm:') + plt.plot([murangeExp[0][0], murangeExp[1][0]], + [murangeExp[1][1]] * 2, 'm:') + plt.plot([murangeExp[1][0]] * 2, + [murangeExp[1][1], murangeExp[0][1]], 'm:') + plt.plot([murangeExp[1][0], murangeExp[0][0]], + [murangeExp[0][1]] * 2, 'm:') + plt.colorbar(p) + plt.title("log10|u(mu)|") + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + p = plt.contourf(Mu1, Mu2, T, cmap = cmap, + levels = np.linspace(ZTmin, ZTmax, 50)) + if clip > 0: + plt.contour(Mu1, Mu2, T, [ZTmin]) + plt.plot(mu2x, mu2y, 'kx') + plt.plot([murangeExp[0][0]] * 2, + [murangeExp[0][1], murangeExp[1][1]], 'm:') + plt.plot([murangeExp[0][0], murangeExp[1][0]], + [murangeExp[1][1]] * 2, 'm:') + plt.plot([murangeExp[1][0]] * 2, + [murangeExp[1][1], murangeExp[0][1]], 'm:') + plt.plot([murangeExp[1][0], murangeExp[0][0]], + [murangeExp[0][1]] * 2, 'm:') + plt.title("log10|u_app(mu)|") + plt.colorbar(p) + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + p = plt.contourf(Mu1, Mu2, R, levels = np.linspace(Rmin, Rmax, 50)) + plt.plot(mu2x, mu2y, 'kx') + plt.plot([murangeExp[0][0]] * 2, + [murangeExp[0][1], murangeExp[1][1]], 'm:') + plt.plot([murangeExp[0][0], murangeExp[1][0]], + [murangeExp[1][1]] * 2, 'm:') + plt.plot([murangeExp[1][0]] * 2, + [murangeExp[1][1], murangeExp[0][1]], 'm:') + plt.plot([murangeExp[1][0], murangeExp[0][0]], + [murangeExp[0][1]] * 2, 'm:') + if normalizeDen: + plt.title("log10|Q(mu)res(mu)|") + else: + plt.title("log10|res(mu)|") + plt.colorbar(p) + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + p = plt.contourf(Mu1, Mu2, E, levels = np.linspace(Emin, Emax, 50)) + plt.plot(mu2x, mu2y, 'kx') + plt.plot([murangeExp[0][0]] * 2, + [murangeExp[0][1], murangeExp[1][1]], 'm:') + plt.plot([murangeExp[0][0], murangeExp[1][0]], + [murangeExp[1][1]] * 2, 'm:') + plt.plot([murangeExp[1][0]] * 2, + [murangeExp[1][1], murangeExp[0][1]], 'm:') + plt.plot([murangeExp[1][0], murangeExp[0][0]], + [murangeExp[0][1]] * 2, 'm:') + if normalizeDen: + plt.title("log10|Q(mu)err(mu)|") + else: + plt.title("log10|err(mu)|") + plt.colorbar(p) + plt.grid() + plt.show() + + if not np.isnan(beta[0, 0]): + plt.figure(figsize = (15, 7)) + plt.jet() + p = plt.contourf(Mu1, Mu2, beta, + levels = np.linspace(betamin, betamax, 50)) + plt.plot(mu2x, mu2y, 'kx') + plt.plot([murangeExp[0][0]] * 2, + [murangeExp[0][1], murangeExp[1][1]], 'm:') + plt.plot([murangeExp[0][0], murangeExp[1][0]], + [murangeExp[1][1]] * 2, 'm:') + plt.plot([murangeExp[1][0]] * 2, + [murangeExp[1][1], murangeExp[0][1]], 'm:') + plt.plot([murangeExp[1][0], murangeExp[0][0]], + [murangeExp[0][1]] * 2, 'm:') + plt.title("beta(mu)") + plt.colorbar(p) + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + p = plt.contourf(Mu1, Mu2, R - np.log10(beta), + levels = np.linspace(Emin, Emax, 50)) + plt.plot(mu2x, mu2y, 'kx') + plt.plot([murangeExp[0][0]] * 2, + [murangeExp[0][1], murangeExp[1][1]], 'm:') + plt.plot([murangeExp[0][0], murangeExp[1][0]], + [murangeExp[1][1]] * 2, 'm:') + plt.plot([murangeExp[1][0]] * 2, + [murangeExp[1][1], murangeExp[0][1]], 'm:') + plt.plot([murangeExp[1][0], murangeExp[0][0]], + [murangeExp[0][1]] * 2, 'm:') + if normalizeDen: + plt.title("log10|Q(mu)res(mu)/beta(mu)|") + else: + plt.title("log10|res(mu)/beta(mu)|") + plt.colorbar(p) + plt.grid() + plt.show() + + plt.figure(figsize = (15, 7)) + plt.jet() + p = plt.contourf(Mu1, Mu2, R - np.log10(beta) - E, 50) + plt.plot(mu2x, mu2y, 'kx') + plt.plot([murangeExp[0][0]] * 2, + [murangeExp[0][1], murangeExp[1][1]], 'm:') + plt.plot([murangeExp[0][0], murangeExp[1][0]], + [murangeExp[1][1]] * 2, 'm:') + plt.plot([murangeExp[1][0]] * 2, + [murangeExp[1][1], murangeExp[0][1]], 'm:') + plt.plot([murangeExp[1][0], murangeExp[0][0]], + [murangeExp[0][1]] * 2, 'm:') + plt.title("log10|eta(mu)|") + plt.colorbar(p) + plt.grid() + plt.show() + +def plotInfSet3(murange, murangeEff, approx, mu0, nSamples = 200, + marginal = [None, None, .05], exps = [2., 1., 1.], clip = -1, + relative = True, normalizeDen = False, nobeta = False): + mNone = [i for i, m in enumerate(marginal) if m is None] + nNone = len(mNone) + if nNone not in [1, 2]: + raise + if nNone == 1: + exp = exps[mNone[0]] + mu1 = np.linspace(murangeEff[0][mNone[0]] ** exp, + murangeEff[1][mNone[0]] ** exp, nSamples) + mu1s = np.power(mu1, 1. / exp) + mus = [] + mBase = copy(marginal) + for m1 in mu1s: + mBase[mNone[0]] = m1 + mus += [copy(mBase)] + Z = approx.normHF(mus) + T = approx.normApprox(mus) + R = approx.normRes(mus) + E = approx.normErr(mus) + if relative: + F = approx.normRHS(mus) + R /= F + E /= Z + if normalizeDen: + Qvals = np.abs(approx.trainedModel.getQVal(mus)) + R *= Qvals + E *= Qvals + if nobeta: + beta = np.empty(len(mus)) + beta[:] = np.nan + else: + beta = approx.HFEngine.stabilityFactor(approx.getHF(mus), mus) + plotInfSet31FromData(mNone, mus, Z, T, R, E, beta, murange, approx, + mu0, exp, normalizeDen) + return mus, Z, T, R, E, beta + if nNone == 2: + exps = [exps[m] for m in mNone] + mu1 = np.linspace(murangeEff[0][mNone[0]] ** exps[0], + murangeEff[1][mNone[0]] ** exps[0], nSamples) + mu2 = np.linspace(murangeEff[0][mNone[1]] ** exps[1], + murangeEff[1][mNone[1]] ** exps[1], nSamples) + mu1s = np.power(mu1, 1. / exps[0]) + mu2s = np.power(mu2, 1. / exps[1]) + Mu1, Mu2 = np.meshgrid(np.real(mu1), np.real(mu2)) + mus = [] + mBase = copy(marginal) + for m2 in mu2s: + mBase[mNone[1]] = m2 + for m1 in mu1s: + mBase[mNone[0]] = m1 + mus += [copy(mBase)] + Ze = approx.normHF(mus).reshape((nSamples, nSamples)) + Te = approx.normApprox(mus).reshape((nSamples, nSamples)) + Re = approx.normRes(mus).reshape((nSamples, nSamples)) + Ee = approx.normErr(mus).reshape((nSamples, nSamples)) + if relative: + Fe = approx.normRHS(mus).reshape((nSamples, nSamples)) + Re /= Fe + Ee /= Ze + if normalizeDen: + Qvals = np.abs(approx.trainedModel.getQVal(mus).reshape( + (nSamples, nSamples))) + Re *= Qvals + Ee *= Qvals + if nobeta: + betae = np.empty((nSamples, nSamples)) + betae[:, :] = np.nan + else: + betae = approx.HFEngine.stabilityFactor(approx.getHF(mus), mus)\ + .reshape((nSamples, nSamples)) + plotInfSet32FromData(mNone, mus, Ze, Te, Re, Ee, betae, murange, + approx, mu0, exps, clip, normalizeDen) + return mus, Ze, Te, Re, Ee, betae + diff --git a/examples/3d/pod/plot_zero_set.py b/examples/3d/pod/plot_zero_set.py new file mode 120000 index 0000000..6b5351e --- /dev/null +++ b/examples/3d/pod/plot_zero_set.py @@ -0,0 +1 @@ +/home/pradovera/Repos/RROMPy/examples/2d/pod/plot_zero_set.py \ No newline at end of file diff --git a/examples/3d/pod/plot_zero_set_3.py b/examples/3d/pod/plot_zero_set_3.py new file mode 100644 index 0000000..c1db350 --- /dev/null +++ b/examples/3d/pod/plot_zero_set_3.py @@ -0,0 +1,170 @@ +import warnings +from copy import deepcopy as copy +import numpy as np +from matplotlib import pyplot as plt + +def plotZeroSet3(murange, murangeEff, approx, mu0, nSamples = 200, + marginal = [None, None, .05], exps = [2., 1., 1.], + clip = -1, polesImTol : float = 1e-5): + mNone = [i for i, m in enumerate(marginal) if m is None] + nNone = len(mNone) + if nNone not in [1, 2, 3]: + raise + if nNone == 1: + exp = exps[mNone[0]] + if hasattr(approx, "mus"): + mu2x = approx.mus(mNone[0]) ** exp + else: + mu2x = mu0[0] ** exp + murangeExp = [[murange[0][mNone[0]] ** exp], + [murange[1][mNone[0]] ** exp]] + mu1 = np.linspace(murangeEff[0][mNone[0]] ** exp, + murangeEff[1][mNone[0]] ** exp, nSamples) + mu1s = np.power(mu1, 1. / exp) + mus = [] + mBase = copy(marginal) + for m1 in mu1s: + mBase[mNone[0]] = m1 + mus += [copy(mBase)] + mu1 = np.real(mu1) + Z = approx.trainedModel.getQVal(mus) + Zabs = np.abs(Z) + Zmin, Zmax = np.min(Zabs), np.max(Zabs) + plt.figure(figsize = (15, 7)) + plt.jet() + plt.semilogy(mu1, Zabs) + for l_ in approx.trainedModel.getPoles(marginal): + plt.plot([np.real(l_ ** exp)] * 2, [Zmin, Zmax], 'b--') + plt.plot(mu2x, [Zmax] * len(mu2x), 'kx') + plt.plot([murangeExp[0][0]] * 2, [Zmin, Zmax], 'm:') + plt.plot([murangeExp[1][0]] * 2, [Zmin, Zmax], 'm:') + plt.xlim(mu1[0], mu1[-1]) + plt.title("|Q(mu)|") + plt.grid() + plt.show() + return mus, Z + if nNone == 2: + exps = [exps[m] for m in mNone] + if hasattr(approx, "mus"): + mu2x = approx.mus(mNone[0]) ** exps[0] + mu2y = approx.mus(mNone[1]) ** exps[1] + else: + mu2x, mu2y = mu0[mNone[0]] ** exps[0], mu0[mNone[1]] ** exps[1] + murangeExp = [[murange[0][mNone[0]] ** exps[0], + murange[0][mNone[1]] ** exps[1]], + [murange[1][mNone[0]] ** exps[0], + murange[1][mNone[1]] ** exps[1]]] + mu1 = np.linspace(murangeEff[0][mNone[0]] ** exps[0], + murangeEff[1][mNone[0]] ** exps[0], nSamples) + mu2 = np.linspace(murangeEff[0][mNone[1]] ** exps[1], + murangeEff[1][mNone[1]] ** exps[1], nSamples) + mu1s = np.power(mu1, 1. / exps[0]) + mu2s = np.power(mu2, 1. / exps[1]) + Mu1, Mu2 = np.meshgrid(np.real(mu1), np.real(mu2)) + mus = [] + mBase = copy(marginal) + for m2 in mu2s: + mBase[mNone[1]] = m2 + for m1 in mu1s: + mBase[mNone[0]] = m1 + mus += [copy(mBase)] + poles1, poles2 = [], [] + for m in mus: + m[mNone[0]] = None + pls = approx.getPoles(m) ** exps[0] + pls[np.imag(pls) > polesImTol] = np.nan + pls = np.real(pls) + poles1 += list(pls) + poles2 += [m[mNone[1]] ** exps[1]] * len(pls) + try: + Z = approx.trainedModel.getQVal(mus).reshape(Mu1.shape) + Zabs = np.log10(np.abs(Z)) + Zabsmin, Zabsmax = np.min(Zabs), np.max(Zabs) + if clip > 0: + Zabsmin += clip * (Zabsmax - Zabsmin) + cmap = plt.cm.bone_r + else: + cmap = plt.cm.jet + except: + Z = None + cmap = plt.cm.jet + warnings.simplefilter("ignore", category = UserWarning) + plt.figure(figsize = (15, 7)) + plt.jet() + if Z is not None: + p = plt.contourf(Mu1, Mu2, Zabs, cmap = cmap, + levels = np.linspace(Zabsmin, Zabsmax, 50)) + if clip > 0: + plt.contour(Mu1, Mu2, Zabs, [Zabsmin]) + plt.plot(poles1, poles2, 'k.') + plt.plot(mu2x, mu2y, 'kx') + plt.plot([murangeExp[0][0]] * 2, + [murangeExp[0][1], murangeExp[1][1]], 'm:') + plt.plot([murangeExp[0][0], murangeExp[1][0]], + [murangeExp[1][1]] * 2, 'm:') + plt.plot([murangeExp[1][0]] * 2, + [murangeExp[1][1], murangeExp[0][1]], 'm:') + plt.plot([murangeExp[1][0], murangeExp[0][0]], + [murangeExp[0][1]] * 2, 'm:') + if Z is not None: + plt.colorbar(p) + plt.xlim(murangeExp[0][0], murangeExp[1][0]) + plt.ylim(murangeExp[0][1], murangeExp[1][1]) + plt.title("log10|Q(mu)|") + plt.grid() + plt.show() + return mus, Z + if nNone == 3: + exps = [exps[m] for m in mNone] + murangeExp = [[murange[0][mNone[0]] ** exps[0], + murange[0][mNone[1]] ** exps[1], + murange[0][mNone[2]] ** exps[2]], + [murange[1][mNone[0]] ** exps[0], + murange[1][mNone[1]] ** exps[1], + murange[1][mNone[2]] ** exps[2]]] + mu1 = np.linspace(murangeEff[0][mNone[0]] ** exps[0], + murangeEff[1][mNone[0]] ** exps[0], nSamples) + mu2 = np.linspace(murangeEff[0][mNone[1]] ** exps[1], + murangeEff[1][mNone[1]] ** exps[1], nSamples) + mu3 = np.linspace(murangeEff[0][mNone[2]] ** exps[2], + murangeEff[1][mNone[2]] ** exps[2], nSamples) + mu1s = np.power(mu1, 1. / exps[0]) + mu2s = np.power(mu2, 1. / exps[1]) + mu3s = np.power(mu3, 1. / exps[2]) + + Mu1, Mu2, Mu3 = np.meshgrid(np.real(mu1), np.real(mu2), np.real(mu3), + indexing = 'ij') + mus = [] + mBase = copy(marginal) + for m2 in mu2s: + mBase[mNone[1]] = m2 + for m3 in mu3s: + mBase[mNone[2]] = m3 + mus += [copy(mBase)] + + poles1, poles2, poles3 = [], [], [] + for m in mus: + m[mNone[0]] = None + pls = approx.getPoles(m) ** exps[0] + pls[np.imag(pls) > polesImTol] = np.nan + pls = np.real(pls) + pls[pls < murangeExp[0][0]] = np.nan + pls[pls > murangeExp[1][0]] = np.nan + poles1 += list(pls) + poles2 += [m[mNone[1]] ** exps[1]] * len(pls) + poles3 += [m[mNone[2]] ** exps[2]] * len(pls) + pts = np.empty((len(poles1), 3)) + pts[:, 0] = poles1 + pts[:, 1] = poles2 + pts[:, 2] = poles3 + if np.size(pts) > 0: + from mpl_toolkits.mplot3d import Axes3D + fig = plt.figure(figsize = (8, 6)) + ax = Axes3D(fig) + ax.scatter(pts[:, 0], pts[:, 1], pts[:, 2], '.', s = 1) + ax.set_xlim(murangeExp[0][0], murangeExp[1][0]) + ax.set_ylim(murangeExp[0][1], murangeExp[1][1]) + ax.set_zlim(murangeExp[0][2], murangeExp[1][2]) + plt.show() + plt.close() + return pts diff --git a/examples/3d/pod/scattering1_pod.py b/examples/3d/pod/scattering1_pod.py new file mode 100644 index 0000000..7ac695f --- /dev/null +++ b/examples/3d/pod/scattering1_pod.py @@ -0,0 +1,190 @@ +import numpy as np +from mpl_toolkits.mplot3d import Axes3D +from matplotlib import pyplot as plt +from rrompy.hfengines.linear_problem.tridimensional import Scattering1d as S1D +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB +from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, + QuadratureSamplerTotal as QST, + ManualSampler as MS, + RandomSampler as RS) + +verb = 50 +size = 2 +show_sample = True +show_norm = False +clip = -1 +#clip = .4 +#clip = .6 +homogeneize = False +#homogeneize = True + +Delta = 0 +MN = 15 +R = (MN + 3) * (MN + 2) * (MN + 1) // 6 +S = [int(np.ceil(R ** (1. / 3.)))] * 3 +PODTol = 1e-8 + +samples = "centered" +samples = "standard" +samples, nDer = "standard_MMM", 15 +algo = "rational" +#algo = "RB" +sampling = "quadrature" +sampling = "quadrature_total" +sampling = "random" + +if samples == "standard": + radial = 0 +# radial = "gaussian" +# radial = "thinplate" +# radial = "multiquadric" + rW0 = 5. + radialWeight = [rW0] * 2 + +assert Delta <= 0 + +if size == 1: + mu0 = [4., np.pi, 0.] + mutar = [4.05, .95 * np.pi, .2] + murange = [[2., .9 * np.pi, -.5], [6., 1.1 * np.pi, .5]] +if size == 2: + mu0 = [4., np.pi, .375] + mutar = [4.05, .95 * np.pi, .2] + murange = [[2., .9 * np.pi, 0.], [6., 1.1 * np.pi, .75]] + +aEff = 1.#25 +bEff = 1. - aEff +murangeEff = [[aEff*murange[0][0] + bEff*murange[1][0], + aEff*murange[0][1] + bEff*murange[1][1], + aEff*murange[0][2] + bEff*murange[1][2]], + [aEff*murange[1][0] + bEff*murange[0][0], + aEff*murange[1][1] + bEff*murange[0][1], + aEff*murange[1][2] + bEff*murange[0][2]]] + +n = 100 + +solver = S1D(mu0 = mu0, n = n, verbosity = verb) + +if algo == "rational": + params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True} + if samples == "standard": + params['polybasis'] = "CHEBYSHEV" +# params['polybasis'] = "LEGENDRE" +# params['polybasis'] = "MONOMIAL" + params['E'] = MN + params['radialBasis'] = radial + params['radialBasisWeights'] = radialWeight + elif samples == "centered": + params['polybasis'] = "MONOMIAL" + params['S'] = R + else: #MMM + params['S'] = nDer * int(np.ceil(R / nDer)) + method = RI +else: #if algo == "RB": + params = {'R':(MN + 3 + Delta) * (MN + 2 + Delta) * (MN + 1 + Delta) // 6, + 'S':S, 'POD':True, 'PODTolerance':PODTol} + if samples == "standard": + pass + elif samples == "centered": + params['S'] = R + else: #MMM + params['S'] = nDer * int(np.ceil(R / nDer)) + method = RB + +if samples == "standard": + if sampling == "quadrature": + params['sampler'] = QS(murange, "CHEBYSHEV") +# params['sampler'] = QS(murange, "GAUSSLEGENDRE") +# params['sampler'] = QS(murange, "UNIFORM") + params['S'] = [max(j, MN + 1) for j in params['S']] + elif sampling == "quadrature_total": + params['sampler'] = QST(murange, "CHEBYSHEV") + params['S'] = R + else: # if sampling == "random": + params['sampler'] = RS(murange, "HALTON") + params['S'] = R +elif samples == "centered": + params['sampler'] = MS(murange, points = [mu0]) +elif samples == "standard_MMM": + if sampling == "quadrature": + SdirEff = int(np.ceil(int(np.ceil(R / nDer)) ** (1. / 3))) + pts = QS(murange, "CHEBYSHEV").generatePoints([SdirEff] * 3) + elif sampling == "quadrature_total": + pts = QST(murange, "CHEBYSHEV").generatePoints(int(np.ceil(R / nDer))) + else: # if sampling == "random": + pts = RS(murange, "HALTON").generatePoints(int(np.ceil(R / nDer))) + params['sampler'] = MS(murange, points = pts) + +approx = method(solver, mu0 = mu0, approxParameters = params, + verbosity = verb, homogeneized = homogeneize) +if samples == "standard": approx.samplingEngine.allowRepeatedSamples = False + +approx.setupApprox() +if show_sample: + import fenics as fen + x = fen.SpatialCoordinate(solver.V.mesh()) + warps = [x * mutar[1] / solver._L - x, x * solver._L / mutar[1] - x] + approx.plotApprox(mutar, warps, name = 'u_app', + homogeneized = False, what = "REAL") + approx.plotHF(mutar, warps, name = 'u_HF', + homogeneized = False, what = "REAL") + approx.plotErr(mutar, warps, name = 'err', + homogeneized = False, what = "REAL") +# approx.plotRes(mutar, warps, name = 'res', +# homogeneized = False, what = "REAL") + appErr = approx.normErr(mutar, homogeneized = homogeneize) + solNorm = approx.normHF(mutar, homogeneized = homogeneize) + resNorm = approx.normRes(mutar, homogeneized = homogeneize) + RHSNorm = approx.normRHS(mutar, homogeneized = homogeneize) + print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, + np.divide(appErr, solNorm))) + print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, + np.divide(resNorm, RHSNorm))) + +fig = plt.figure(figsize = (8, 6)) +ax = Axes3D(fig) +ax.scatter(approx.trainedModel.data.mus(0), approx.trainedModel.data.mus(1), + approx.trainedModel.data.mus(2), '.') +ax.set_xlim3d(murangeEff[0][0], murangeEff[1][0]) +ax.set_ylim3d(murangeEff[0][1], murangeEff[1][1]) +ax.set_zlim3d(murangeEff[0][2], murangeEff[1][2]) +plt.show() +plt.close() + +approx.verbosity = 0 +approx.trainedModel.verbosity = 0 +if algo == "rational" and approx.N > 0: + from plot_zero_set_3 import plotZeroSet3 + muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, + [None, mu0[1], mu0[2]], [1., 1., 1.], + clip = clip) + muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, + [None, None, mu0[2]], [1., 1., 1.], + clip = clip) + muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, + [None, mu0[1], None], [1., 1., 1.], + clip = clip) + plotZeroSet3(murange, murangeEff, approx, mu0, 100, [None, None, None], + [1., 1., 1.], clip = clip, imagTol = 1e-2) + +if show_norm: + solver._solveBatchSize = 25 + from plot_inf_set_3 import plotInfSet3 + muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( + murange, murangeEff, approx, mu0, 25, + [None, mu0[1], mu0[2]], [1., 1., 1.], + clip = clip, relative = False, + normalizeDen = True) + muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( + murange, murangeEff, approx, mu0, 25, + [None, None, mu0[2]], [1., 1., 1.], + clip = clip, relative = False, + normalizeDen = True) + muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( + murange, murangeEff, approx, mu0, 25, + [None, mu0[1], None], [1., 1., 1.], + clip = clip, relative = False, + normalizeDen = True) + +print(approx.getPoles([None, np.pi, 0.])) diff --git a/examples/airfoil/greedy.py b/examples/airfoil/greedy.py index 373d19c..1f23b37 100644 --- a/examples/airfoil/greedy.py +++ b/examples/airfoil/greedy.py @@ -1,106 +1,109 @@ import numpy as np from airfoil_engine import AirfoilScatteringEngine -from rrompy.reduction_methods.distributed_greedy import \ - RationalInterpolantGreedy as Pade -from rrompy.reduction_methods.distributed_greedy import \ - RBDistributedGreedy as RB +from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI +from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB from rrompy.solver.fenics import L2NormMatrix +from rrompy.parameter.parameter_sampling import QuadratureSampler as QS -verb = 2 +verb = 5 timed = False -algo = "Pade" +algo = "RI" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" homog = True homog = False -k0s = np.linspace(5, 20, 100) +k0s = np.linspace(5, 10, 100) k0 = np.mean(k0s) kl, kr = min(k0s), max(k0s) -params = {'muBounds':[kl, kr], 'nTestPoints':500, 'Delta':0, - 'greedyTol':1e-2, 'S':2, 'basis':polyBasis} +params = {'S':5, 'sampler':QS([kl, kr], "UNIFORM"), 'nTestPoints':500, + 'greedyTol':1e-2, 'polybasis':polyBasis, + 'errorEstimatorKind':'BASIC'} ######### 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) +if algo == "RI": + approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb, + homogeneized = homog) else: + params.pop("polybasis") + params.pop("errorEstimatorKind") approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) res[j] = (approx.estimatorNormEngine.norm( - approx.getRes(k0s[j], homogeneized=homog)) + approx.getRes(k0s[j], homogeneized = homog, duality = False)) / approx.estimatorNormEngine.norm( - approx.getRHS(k0s[j], homogeneized=homog))) + approx.getRHS(k0s[j], homogeneized = homog, duality = False))) 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.plot(np.real(approx.mus.data), + 1.05*np.max(norm)*np.ones_like(approx.mus.data, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') -plt.semilogy(np.real(approx.mus), - 4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx') +plt.semilogy(np.real(approx.mus.data), + 4.*np.max(resApp)*np.ones_like(approx.mus.data, dtype = float), + 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/airfoil/pod.py b/examples/airfoil/pod.py index 099ee00..c51576b 100644 --- a/examples/airfoil/pod.py +++ b/examples/airfoil/pod.py @@ -1,110 +1,108 @@ import numpy as np from airfoil_engine import AirfoilScatteringEngine -from rrompy.reduction_methods.distributed import RationalInterpolant as Pade -from rrompy.reduction_methods.distributed import RBDistributed as RB +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS verb = 100 homog = True homog = False sol = "single" sol = "sweep" -algo = "Pade" +algo = "RI" algo = "RB" polyBasis = "LEGENDRE" polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" Nsweep = 100 k0s = [x * 2 * np.pi / 340 for x in [1.0e2, 5.0e2]] k0 = np.mean(np.power(k0s, 2.)) ** .5 ktar = k0s[0] + (k0s[1] - k0s[0]) * .7 params = {'N':29, 'M':29, 'R':30, 'S':30, 'POD':True, 'polybasis':polyBasis, 'sampler':QS(k0s, "CHEBYSHEV"), 'robustTol':1e-14} theta = - 45. * np.pi / 180 solver = AirfoilScatteringEngine(k0, theta, verbosity = verb, degree_threshold = 8) -if algo == "Pade": +if algo == "RI": params.pop('R') - approx = Pade(solver, mu0 = k0, approxParameters = params, - verbosity = verb) + approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: params.pop('N') params.pop('M') params.pop('polybasis') params.pop('robustTol') - approx = RB(solver, mu0 = k0, approxParameters = params, - verbosity = verb) + approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() if sol == "single": # approx.outParaviewTimeDomainSamples(filename = "out/outSamples", # forceNewFile = False, folders = True) approx.outParaviewTimeDomainApprox(ktar, omega = 2. * np.pi * ktar, filename = "out/outTApp{}".format(ktar), forceNewFile = False, folder = True) approx.outParaviewTimeDomainHF(ktar, omega = 2. * np.pi * ktar, filename = "out/outTHF{}".format(ktar), forceNewFile = False, folder = True) approx.outParaviewTimeDomainErr(ktar, omega = 2. * np.pi * ktar, filename = "out/outTErr{}".format(ktar), forceNewFile = False, folder = True) approx.outParaviewTimeDomainRes(ktar, omega = 2. * np.pi * ktar, filename = "out/outTRes{}".format(ktar), forceNewFile = False, folder = True) appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('Poles:', approx.getPoles()) if sol == "sweep": k0s = np.linspace(k0s[0], k0s[1], Nsweep) kl, kr = min(k0s), max(k0s) approx.samplingEngine.verbosity = 0 approx.trainedModel.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) err[j] = approx.normErr(k0s[j]) / norm[j] plt.figure() plt.semilogy(k0s, norm) plt.semilogy(k0s, normApp, '--') plt.semilogy(np.real(approx.mus), 1.05*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/all_forcing/all_forcing_engine.py b/examples/all_forcing/all_forcing_engine.py new file mode 100644 index 0000000..85bc985 --- /dev/null +++ b/examples/all_forcing/all_forcing_engine.py @@ -0,0 +1,49 @@ +import numpy as np +import fenics as fen +from rrompy.hfengines.linear_problem import LaplaceBaseProblemEngine as LBPE +from rrompy.utilities.base import verbosityManager as vbMng +from rrompy.solver.fenics import fenics2Vector + +class AllForcingEngine(LBPE): + def __init__(self, mu0:float, n : int = 30, + degree_threshold : int = np.inf, + verbosity : int = 10, timestamp : bool = True): + super().__init__(mu0 = mu0, degree_threshold = degree_threshold, + verbosity = verbosity, timestamp = timestamp) + mesh = fen.RectangleMesh(fen.Point(-5., -5.), fen.Point(5., 5.), n, n) + self.V = fen.FunctionSpace(mesh, "P", 1) + self.nAs, self.nbs = 1, 4 + x, y = fen.SpatialCoordinate(mesh)[:] + scaling = (2. * np.pi) ** -1. + r2 = x ** 2. + y ** 2. + self.forcingCoeffs = [ + scaling * fen.exp(- (r2 + 1. - 2. * x + 1. - 2. * y) / 2. / 4.) / 2., + scaling * fen.exp(- (r2 + 1. + 2. * x + 1. + 2. * y) / 2. / 16.) / 4., + - scaling * fen.exp(- (r2 + 1. + 2. * x + 1. - 2. * y) / 2. / 9.) / 30., + scaling * fen.exp(- (r2 + 1. - 2. * x + 1. + 2. * y) / 2. / 25.) / 120.] + + def b(self, mu = [], der = 0, homogeneized = False): + mu = self.checkParameter(mu) + if not hasattr(der, "__len__"): der = [der] * self.npar + derI = der[0] + nbsTot = self.nbsH if homogeneized else self.nbs + bs = self.bsH if homogeneized else self.bs + if homogeneized and self.mu0 != self.mu0BC: + self.liftDirichletData(self.mu0) + for j in range(derI, nbsTot): + if bs[j] is None: + vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), + 20) + parsRe = self.iterReduceQuadratureDegree([( + self.forcingCoeffs[j], + "forcingCoefficient")]) + u0Re = self.DirichletDatum[0] + L0Re = fen.dot(self.forcingCoeffs[j], self.v) * fen.dx + DBCR = fen.DirichletBC(self.V, u0Re, self.DirichletBoundary) + b = fenics2Vector(L0Re, parsRe, DBCR, 1) + if homogeneized: + self.bsH[j] = b + else: + self.bs[j] = b + vbMng(self, "DEL", "Done assembling forcing term.", 20) + return self._assembleb(mu, der, derI, homogeneized) diff --git a/examples/all_forcing/greedy.py b/examples/all_forcing/greedy.py new file mode 100644 index 0000000..b32a4cc --- /dev/null +++ b/examples/all_forcing/greedy.py @@ -0,0 +1,96 @@ +import numpy as np +from all_forcing_engine import AllForcingEngine +from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI +from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB +from rrompy.solver.fenics import L2NormMatrix +from rrompy.parameter.parameter_sampling import QuadratureSampler as QS + +verb = 5 +timed = False +algo = "RI" +#algo = "RB" +polyBasis = "LEGENDRE" +#polyBasis = "CHEBYSHEV" +#polyBasis = "MONOMIAL" +if timed: verb = 0 + +z0s = np.linspace(-3., 3., 100) +z0 = np.mean(z0s) +zl, zr = min(z0s), max(z0s) + +n = 30 +solver = AllForcingEngine(mu0 = z0, n = n, degree_threshold = 8, verbosity = 0) + +params = {'sampler':QS([zl, zr], "UNIFORM"), 'nTestPoints':500, + 'greedyTol':1e-2, 'S':2, 'polybasis':polyBasis, +# 'errorEstimatorKind':'BARE'} +# 'errorEstimatorKind':'BASIC'} + 'errorEstimatorKind':'EXACT'} + +if algo == "RI": + approx = RI(solver, mu0 = solver.mu0, approxParameters = params, + verbosity = verb) +else: + params.pop("polybasis") + params.pop("errorEstimatorKind") + approx = RB(solver, mu0 = solver.mu0, approxParameters = params, + verbosity = verb) +approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) + +if timed: + from time import clock + start_time = clock() + approx.greedy() + print("Time: ", clock() - start_time) +else: + approx.greedy(True) +polesApp = approx.getPoles() +print("Poles:\n", polesApp) + +approx.samplingEngine.verbosity = 0 +approx.trainedModel.verbosity = 0 +approx.verbosity = 0 +zl, zr = np.real(zl), np.real(zr) +from matplotlib import pyplot as plt +normApp = np.zeros(len(z0s)) +norm = np.zeros_like(normApp) +res = np.zeros_like(normApp) +err = np.zeros_like(normApp) +for j in range(len(z0s)): + normApp[j] = approx.normApprox(z0s[j]) + norm[j] = approx.normHF(z0s[j]) + res[j] = (approx.estimatorNormEngine.norm( + approx.getRes(z0s[j], duality = False)) + / approx.estimatorNormEngine.norm( + approx.getRHS(z0s[j], duality = False))) + err[j] = approx.normErr(z0s[j]) / norm[j] +resApp = approx.errorEstimator(z0s) + +plt.figure() +plt.semilogy(z0s, norm) +plt.semilogy(z0s, normApp, '--') +plt.semilogy(np.real(approx.mus.data), + 1.05*np.max(norm)*np.ones_like(approx.mus.data, dtype = float), + 'rx') +plt.xlim([zl, zr]) +plt.grid() +plt.show() +plt.close() + +plt.figure() +plt.semilogy(z0s, res) +plt.semilogy(z0s, resApp, '--') +plt.semilogy(np.real(approx.mus.data), + approx.greedyTol*np.ones_like(approx.mus.data, dtype = float), + 'rx') +plt.xlim([zl, zr]) +plt.grid() +plt.show() +plt.close() + +plt.figure() +plt.semilogy(z0s, err) +plt.xlim([zl, zr]) +plt.grid() +plt.show() +plt.close() diff --git a/examples/all_forcing/pod.py b/examples/all_forcing/pod.py new file mode 100644 index 0000000..96163e6 --- /dev/null +++ b/examples/all_forcing/pod.py @@ -0,0 +1,88 @@ +import numpy as np +from all_forcing_engine import AllForcingEngine +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB +from rrompy.parameter.parameter_sampling import QuadratureSampler as QS + +verb = 100 +sol = "single" +sol = "sweep" +algo = "RI" +#algo = "RB" +polyBasis = "LEGENDRE" +polyBasis = "CHEBYSHEV" +#polyBasis = "MONOMIAL" + +ztar = 2. +z0s = [-3., 3.] +z0 = np.mean(z0s) + +n = 30 +solver = AllForcingEngine(mu0 = z0, n = n, degree_threshold = 8, verbosity = 0) + +params = {'N':3, 'M':3, 'S':4, 'POD':True, 'polybasis':polyBasis, + 'sampler':QS(z0s, "CHEBYSHEV")} + +if algo == "RI": + approx = RI(solver, mu0 = z0, approxParameters = params, verbosity = verb) +else: + params.pop("N") + params.pop("M") + params.pop("polybasis") + approx = RB(solver, mu0 = z0, approxParameters = params, verbosity = verb) + +approx.setupApprox() +if sol == "single": + approx.plotSamples(what = "REAL") + approx.plotApprox(ztar, what = "REAL", name = "uApp") + approx.plotHF(ztar, what = "REAL", name = "uHF") + approx.plotErr(ztar, what = "REAL", name = "err") + approx.plotRes(ztar, what = "REAL", name = "res") + + appErr, solNorm = approx.normErr(ztar), approx.normHF(ztar) + resNorm, RHSNorm = approx.normRes(ztar), approx.normRHS(ztar) + 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": + z0s = np.linspace(z0s[0], z0s[1], 100) + zl, zr = min(z0s), max(z0s) + approx.samplingEngine.verbosity = 0 + approx.trainedModel.verbosity = 0 + approx.verbosity = 0 + from matplotlib import pyplot as plt + normRHS = approx.normRHS(z0s) + norm = approx.normHF(z0s) + normApp = approx.normApprox(z0s) + res = approx.normRes(z0s) / normRHS + err = approx.normErr(z0s) / norm + + plt.figure() + plt.semilogy(z0s, norm) + plt.semilogy(z0s, normApp, '--') + plt.semilogy(np.real(approx.mus.data), + 1.05*np.max(norm)*np.ones_like(approx.mus.data, dtype = float), + 'rx') + plt.xlim([zl, zr]) + plt.grid() + plt.show() + plt.close() + + plt.figure() + plt.semilogy(z0s, res) + plt.xlim([zl, zr]) + plt.grid() + plt.show() + plt.close() + + plt.figure() + plt.semilogy(z0s, err) +# plt.semilogy(k0s, errApp) + plt.xlim([zl, zr]) + plt.grid() + plt.show() + plt.close() diff --git a/examples/all_forcing/solver.py b/examples/all_forcing/solver.py new file mode 100644 index 0000000..17d9d1e --- /dev/null +++ b/examples/all_forcing/solver.py @@ -0,0 +1,11 @@ +from all_forcing_engine import AllForcingEngine + +z0 = 0. +ztar = 3. +n = 30 + +solver = AllForcingEngine(mu0 = z0, n = n, degree_threshold = 8, verbosity = 0) +b = solver.b(ztar) +solver.plot(b, what = "REAL", name = "b") +uh = solver.solve(ztar)[0] +solver.plot(uh, what = "REAL") diff --git a/examples/diapason/greedy.py b/examples/diapason/greedy.py index 1d86efe..3121583 100644 --- a/examples/diapason/greedy.py +++ b/examples/diapason/greedy.py @@ -1,178 +1,129 @@ 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.distributed_greedy import \ - RationalInterpolantGreedy as Pade -from rrompy.reduction_methods.distributed_greedy import \ - RBDistributedGreedy as RB +from diapason_engine import DiapasonEngine, DiapasonEngineDamped +from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI +from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB from rrompy.solver.fenics import L2NormMatrix +from rrompy.parameter.parameter_sampling import QuadratureSampler as QS -verb = 2 +verb = 5 timed = False -algo = "Pade" +algo = "RI" #algo = "RB" polyBasis = "LEGENDRE" -polyBasis = "CHEBYSHEV" +#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.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, '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(mu0 = np.real(k0), degree_threshold = 8, verbosity = 0) - solver.eta = dampingEta +if np.isclose(dampingEta, 0.): + solver = DiapasonEngine(kappa = np.mean(np.power(k0s, 2.)) ** .5, c = c, + rho = rho, E = E, nu = nu, T = T, theta = theta, + phi = phi, meshNo = 1, degree_threshold = 8, + verbosity = 0) else: - solver = LEHPE(mu0 = np.real(k0), degree_threshold = 8, verbosity = 0) -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) + solver = DiapasonEngineDamped(kappa = np.mean(k0s), c = c, rho = rho, + E = E, nu = nu, T = T, theta = theta, + phi = phi, dampingEta = dampingEta, + meshNo = 1, degree_threshold = 8, + verbosity = 0) + +params = {'sampler':QS([kl, kr], "UNIFORM"),#, solver.rescalingExp), + 'nTestPoints':500, 'greedyTol':1e-2, 'S':5, 'polybasis':polyBasis, +# 'errorEstimatorKind':'BARE'} +# 'errorEstimatorKind':'BASIC'} + 'errorEstimatorKind':'EXACT'} + +if algo == "RI": + approx = RI(solver, mu0 = solver.mu0, 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, + approx = RB(solver, mu0 = solver.mu0, approxParameters = params, verbosity = verb) approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) polesApp = approx.getPoles() print("Poles:\n", polesApp) approx.samplingEngine.verbosity = 0 +approx.trainedModel.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) - res[j] = (approx.estimatorNormEngine.norm(approx.getRes(k0s[j])) - / approx.estimatorNormEngine.norm(approx.getRHS(k0s[j]))) + res[j] = (approx.estimatorNormEngine.norm( + approx.getRes(k0s[j], duality = False)) + / approx.estimatorNormEngine.norm( + approx.getRHS(k0s[j], duality = False))) 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), +plt.semilogy(np.real(approx.mus.data), + 1.05*np.max(norm)*np.ones_like(approx.mus.data, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') -plt.semilogy(np.real(approx.mus), - approx.greedyTol*np.ones_like(approx.mus, dtype = float), +plt.semilogy(np.real(approx.mus.data), + approx.greedyTol*np.ones_like(approx.mus.data, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesAppEff = polesApp[~mask] plt.figure() plt.plot(np.real(polesAppEff), np.imag(polesAppEff), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/diapason/pod.py b/examples/diapason/pod.py index 3c69eb2..1ba8e43 100644 --- a/examples/diapason/pod.py +++ b/examples/diapason/pod.py @@ -1,149 +1,147 @@ import numpy as np from diapason_engine import DiapasonEngine, DiapasonEngineDamped -from rrompy.reduction_methods.distributed import RationalInterpolant as Pade -from rrompy.reduction_methods.distributed import RBDistributed as RB +from rrompy.reduction_methods.standard import RationalInterpolant as Pade +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS verb = 100 sol = "single" sol = "sweep" algo = "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) + rescalingExp = 2. 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 + rescalingExp = 1. 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, - 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)}#, + 'sampler':QS(k0s, "CHEBYSHEV", rescalingExp)}#, # '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.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/diapason/solver.py b/examples/diapason/solver.py index 9cf3c78..715756b 100644 --- a/examples/diapason/solver.py +++ b/examples/diapason/solver.py @@ -1,36 +1,32 @@ import numpy as np from diapason_engine import DiapasonEngine, DiapasonEngineDamped verb = 2 dampingEta = 0 * 1e4 / 2. / np.pi k0 = 1.8e3 # [Hz] ktar = 1.8e3 # [Hz] 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) uh = solver.solve(ktar)[0] solver.outParaviewTimeDomain(uh, omega = 2. * np.pi * ktar, filename = "out/outT{}_{}_".format(ktar, dampingEta), forceNewFile = False) diff --git a/examples/from_papers/greedy_internalBox.py b/examples/from_papers/greedy_internalBox.py index 3ec5bbe..caa2d7f 100644 --- a/examples/from_papers/greedy_internalBox.py +++ b/examples/from_papers/greedy_internalBox.py @@ -1,111 +1,114 @@ import numpy as np import fenics as fen from rrompy.hfengines.linear_problem import HelmholtzProblemEngine as HPE -from rrompy.reduction_methods.distributed_greedy import \ - RationalInterpolantGreedy as Pade -from rrompy.reduction_methods.distributed_greedy import \ - RBDistributedGreedy as RB +from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI +from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB from rrompy.solver.fenics import L2NormMatrix +from rrompy.parameter.parameter_sampling import QuadratureSampler as QS dim = 3 -verb = 2 +verb = 5 timed = False -algo = "Pade" -algo = "RB" +algo = "RI" +#algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" k0s = np.power(np.linspace(500 ** 2., 2250 ** 2., 200), .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 kl, kr = min(k0s), max(k0s) -params = {'muBounds':[kl, kr], 'nTestPoints':500, 'Delta':0, - 'greedyTol':1e-2, 'S':2, 'basis':polyBasis} +params = {'sampler':QS([kl, kr], "UNIFORM", 2.), 'nTestPoints':500, + 'greedyTol':1e-2, 'S':2, 'polybasis':polyBasis, + 'errorEstimatorKind':'BASIC'} if dim == 2: mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(.1, .15), 10, 15) x, y = fen.SpatialCoordinate(mesh)[:] f = fen.exp(- 1e2 * (x + y)) else:#if dim == 3: mesh = fen.BoxMesh(fen.Point(0., 0., 0.), fen.Point(.1, .15, .25), 4, 6,10) x, y, z = fen.SpatialCoordinate(mesh)[:] f = fen.exp(- 1e2 * (x + y + z)) solver = HPE(np.real(k0), verbosity = verb) solver.V = fen.FunctionSpace(mesh, "P", 3) solver.refractionIndex = fen.Constant(1. / 54.6) solver.forcingTerm = f solver.NeumannBoundary = "ALL" ######### -if algo == "Pade": - approx = Pade(solver, mu0 = k0, approxParameters = params, - verbosity = verb) +if algo == "RI": + approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: + params.pop("polybasis") + params.pop("errorEstimatorKind") approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) - res[j] = (approx.estimatorNormEngine.norm(approx.getRes(k0s[j])) - / approx.estimatorNormEngine.norm(approx.getRHS(k0s[j]))) + res[j] = (approx.estimatorNormEngine.norm( + approx.getRes(k0s[j], duality = False)) + / approx.estimatorNormEngine.norm( + approx.getRHS(k0s[j], duality = False))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) plt.figure() plt.plot(k0s, norm) plt.plot(k0s, normApp, '--') plt.plot(np.real(approx.mus(0)), 1.05*np.max(norm)*np.ones(approx.mus.shape, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus(0)), 4.*np.max(resApp)*np.ones(approx.mus.shape, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/from_papers/greedy_scatteringAirfoil.py b/examples/from_papers/greedy_scatteringAirfoil.py index 5b5bd75..ade4b37 100644 --- a/examples/from_papers/greedy_scatteringAirfoil.py +++ b/examples/from_papers/greedy_scatteringAirfoil.py @@ -1,149 +1,153 @@ import numpy as np import fenics as fen import ufl from rrompy.hfengines.linear_problem import \ HelmholtzBoxScatteringProblemEngine as HSP -from rrompy.reduction_methods.distributed_greedy import \ - RationalInterpolantGreedy as Pade -from rrompy.reduction_methods.distributed_greedy import \ - RBDistributedGreedy as RB +from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI +from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB from rrompy.solver.fenics import fenONE, L2NormMatrix +from rrompy.parameter.parameter_sampling import QuadratureSampler as QS verb = 2 timed = False -algo = "Pade" +algo = "RI" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" homog = True homog = False k0s = np.linspace(5, 20, 25) k0 = np.mean(k0s) kl, kr = min(k0s), max(k0s) -params = {'muBounds':[kl, kr], 'nTestPoints':500, 'Delta':0, - 'greedyTol':1e-2, 'S':2, 'polybasis':polyBasis} +params = {'sampler':QS([kl, kr], "UNIFORM"), 'nTestPoints':500, + 'greedyTol':1e-2, 'S':10, 'polybasis':polyBasis, + 'errorEstimatorKind':'BARE'} +# 'errorEstimatorKind':'BASIC'} +# 'errorEstimatorKind':'EXACT'} ######### 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.V = fen.FunctionSpace(mesh, "P", 2) 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) +if algo == "RI": + approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb, + homogeneized = homog) else: + params.pop("polybasis") + params.pop("errorEstimatorKind") approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) res[j] = (approx.estimatorNormEngine.norm( - approx.getRes(k0s[j], homogeneized=homog)) + approx.getRes(k0s[j], homogeneized = homog, duality = False)) / approx.estimatorNormEngine.norm( - approx.getRHS(k0s[j], homogeneized=homog))) + approx.getRHS(k0s[j], homogeneized = homog, duality = False))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) plt.figure() plt.plot(k0s, norm) plt.plot(k0s, normApp, '--') plt.plot(np.real(approx.mus(0)), 1.05*np.max(norm)*np.ones(approx.mus.shape, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus(0)), 4.*np.max(resApp)*np.ones(approx.mus.shape, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/from_papers/pod_scatteringAirfoil.py b/examples/from_papers/pod_scatteringAirfoil.py index 0c5de0b..d0cf6b4 100644 --- a/examples/from_papers/pod_scatteringAirfoil.py +++ b/examples/from_papers/pod_scatteringAirfoil.py @@ -1,144 +1,132 @@ import numpy as np import fenics as fen import ufl from rrompy.hfengines.linear_problem import \ HelmholtzBoxScatteringProblemEngine as HSP -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.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS from rrompy.solver.fenics import fenONE from operator import itemgetter def subdict(d, ks): return dict(zip(ks, itemgetter(*ks)(d))) verb = 0 #################### homog = True #homog = False #################### test = "solve" -test = "Centered" -test = "Distributed" +test = "approx" plotSamples = True k0 = 10 kLeft, kRight = 8 + 0.j, 12 + 0.j ktar = 11 ktars = np.linspace(8, 12, 21) + 0.j PI = np.pi R = 2 def Dboundary(x, on_boundary): return on_boundary and (x[0]**2+x[1]**2)**.5 < .95 * R kappa = 10 theta = PI * - 45 / 180. mu = 1.1 epsilon = .1 mesh = fen.Mesh('../data/mesh/airfoil.xml') c, s = np.cos(theta), np.sin(theta) x, y = fen.SpatialCoordinate(mesh)[:] u0R = - fen.cos(kappa * (c * x + s * y)) u0I = - fen.sin(kappa * (c * x + s * y)) checkReal = x**2-x+y**2 rhop5 = ((x**2+y**2)/((x-1)**2+y**2))**.25 phiroot1 = fen.atan(-y/(x**2-x+y**2)) / 2 phiroot2 = fen.atan(-y/(x**2-x+y**2)) / 2 - PI * ufl.sign(-y/(x**2-x+y**2)) / 2 kappam1 = (((rhop5*fen.cos(phiroot1)+.5)**2.+(rhop5*fen.sin(phiroot1))**2.)/ ((rhop5*fen.cos(phiroot1)-1.)**2.+(rhop5*fen.sin(phiroot1))**2.) )**.5 - mu kappam2 = (((rhop5*fen.cos(phiroot2)+.5)**2.+(rhop5*fen.sin(phiroot2))**2.)/ ((rhop5*fen.cos(phiroot2)-1.)**2.+(rhop5*fen.sin(phiroot2))**2.) )**.5 - mu Heps1 = .9 * .5 * (1 + kappam1/epsilon + fen.sin(PI*kappam1/epsilon) / PI) + .1 Heps2 = .9 * .5 * (1 + kappam2/epsilon + fen.sin(PI*kappam2/epsilon) / PI) + .1 cTT = ufl.conditional(ufl.le(kappam1, epsilon), Heps1, fenONE) c_F = fen.Constant(.1) cFT = ufl.conditional(ufl.le(kappam2, epsilon), Heps2, fenONE) c_F = fen.Constant(.1) cT = ufl.conditional(ufl.ge(kappam1, - epsilon), cTT, c_F) cF = ufl.conditional(ufl.ge(kappam2, - epsilon), cFT, c_F) a = ufl.conditional(ufl.ge(checkReal, 0.), cT, cF) ### solver = HSP(R, np.abs(k0), theta, n = 1, verbosity = verb, degree_threshold = 8) 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)[0] uh = uhtot + uinc else: uh = solver.solve(k0, homogeneized = homog)[0] uhtot = uh - uinc print(solver.norm(uh)) print(solver.norm(uhtot)) solver.plot(fen.project(a, solver.V).vector(), what = 'Real', name = 'a') solver.plot(uinc, what = 'Real', name = 'u_inc') solver.plot(uh, what = 'ABS') solver.plot(uhtot, what = 'ABS', name = 'u + u_inc') -elif test in ["Centered", "Distributed"]: - if test == "Centered": - params = {'N':8, 'M':8, 'R':8, 'E':8, 'POD':True} - parPade = subdict(params, ['N', 'M', 'E', 'POD']) - parRB = subdict(params, ['R', 'E', 'POD']) - approxPade = PC(solver, mu0 = k0, approxParameters = parPade, - verbosity = verb, homogeneized = homog) - approxRB = RBC(solver, mu0 = k0, approxParameters = parRB, - verbosity = verb, homogeneized = homog) - else: - params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, - 'polybasis':"CHEBYSHEV", - 'sampler':QS([kLeft, kRight], "CHEBYSHEV")} - parPade = subdict(params, ['N', 'M', 'S', 'POD', 'polybasis', - 'sampler']) - parRB = subdict(params, ['R', 'S', 'POD', 'sampler']) - approxPade = PD(solver, mu0 = np.mean([kLeft, kRight]), - approxParameters = parPade, verbosity = verb, - homogeneized = homog) - approxRB = RBD(solver, mu0 = np.mean([kLeft, kRight]), - approxParameters = parRB, verbosity = verb, - homogeneized = homog) - - approxPade.setupApprox() +elif test == "approx": + params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, + 'polybasis':"CHEBYSHEV", + 'sampler':QS([kLeft, kRight], "CHEBYSHEV")} + parRI = subdict(params, ['N', 'M', 'S', 'POD', 'polybasis', + 'sampler']) + parRB = subdict(params, ['R', 'S', 'POD', 'sampler']) + approxRI = RI(solver, mu0 = np.mean([kLeft, kRight]), + approxParameters = parRI, verbosity = verb, + homogeneized = homog) + approxRB = RB(solver, mu0 = np.mean([kLeft, kRight]), + approxParameters = parRB, verbosity = verb, + homogeneized = homog) + + approxRI.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''') + approxRI.plotSamples() + approxRI.plotHF(ktar, name = 'u_HF') + approxRI.plotApprox(ktar, name = 'u_RI''') + approxRI.plotErr(ktar, name = 'err_RI''') + approxRI.plotRes(ktar, name = 'res_RI''') approxRB.plotApprox(ktar, name = 'u_RB') approxRB.plotErr(ktar, name = 'err_RB') approxRB.plotRes(ktar, name = 'res_RB') - HFNorm, RHSNorm = approxPade.normHF(ktar), approxPade.normRHS(ktar) - PadeRes, PadeErr = approxPade.normRes(ktar), approxPade.normErr(ktar) + HFNorm, RHSNorm = approxRI.normHF(ktar), approxRI.normRHS(ktar) + RIRes, RIErr = approxRI.normRes(ktar), approxRI.normErr(ktar) RBRes, RBErr = approxRB.normRes(ktar), approxRB.normErr(ktar) print('HFNorm:\t{}\nRHSNorm:\t{}'.format(HFNorm, RHSNorm)) - print('PadeRes:\t{}\nPadeErr:\t{}'.format(PadeRes, PadeErr)) + print('RIRes:\t{}\nRIErr:\t{}'.format(RIRes, RIErr)) print('RBRes:\t{}\nRBErr:\t{}'.format(RBRes, RBErr)) - print('\nPoles Pade'':') - print(approxPade.getPoles()) + print('\nPoles RI'':') + print(approxRI.getPoles()) diff --git a/examples/greedy/matrix_greedy.py b/examples/greedy/matrix_greedy.py deleted file mode 100644 index c8e5db2..0000000 --- a/examples/greedy/matrix_greedy.py +++ /dev/null @@ -1,113 +0,0 @@ -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.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 = 200 -errorEstimatorKind = "BARE" -#errorEstimatorKind = "BASIC" -#errorEstimatorKind = "EXACT" - -N = 100 -solver = MEB(verbosity = verb) -solver.npar = 1 -solver.nAs = 2 -if test == 1: - solver.As = [sp.spdiags([np.arange(1, 1 + N)], [0], N, N), - - sp.eye(N)] -elif test == 2: - solver.setSolver("SOLVE") - fftB = np.fft.fft(np.eye(N)) * N**-.5 - solver.As = [fftB.dot(np.multiply(np.arange(1, 1 + N), fftB.conj()).T), - - np.eye(N)] -np.random.seed(420) -solver.nbs = 1 -solver.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] - -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, 'S':5, - 'greedyTol':1e-2, 'polybasis':"CHEBYSHEV", - 'errorEstimatorKind':errorEstimatorKind} - approx = Pade(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) -elif method == "RB": - params = {'muBounds':murange, 'nTestPoints':500, 'greedyTol':1e-2, 'S':5} - approx = RB(solver, mu0 = mu0, 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 -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.estimatorNormEngine.norm(approx.getRes(mutars[j])) - / approx.estimatorNormEngine.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(0)), - 1.25*np.max(norm)*np.ones(approx.mus.shape, 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(0)), - 4.*np.max(resApp)*np.ones(approx.mus.shape, 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 deleted file mode 100644 index 7284438..0000000 --- a/examples/greedy/parametricDomain.py +++ /dev/null @@ -1,105 +0,0 @@ -import numpy as np -from rrompy.hfengines.linear_problem import \ - HelmholtzSquareBubbleDomainProblemEngine as HSBDPE -from rrompy.reduction_methods.distributed_greedy import \ - RationalInterpolantGreedy as Pade -from rrompy.reduction_methods.distributed_greedy import \ - RBDistributedGreedy as RB -from rrompy.solver.fenics import L2NormMatrix - -verb = 2 -timed = False -algo = "Pade" -algo = "RB" -polyBasis = "LEGENDRE" -polyBasis = "CHEBYSHEV" -#polyBasis = "MONOMIAL" -errorEstimatorKind = "BARE" -#errorEstimatorKind = "BASIC" -#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, 'S':2, 'polybasis':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: - params.pop('Delta') - params.pop('polybasis') - params.pop('errorEstimatorKind') - approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) -approx.initEstimatorNormEngine(np.abs(k0) ** 2. * L2NormMatrix(solver.V)) - -if timed: - from time import clock - start_time = clock() - approx.greedy() - print("Time: ", clock() - start_time) -else: - approx.greedy(True) - -approx.samplingEngine.verbosity = 0 -approx.verbosity = 0 -from matplotlib import pyplot as plt -normApp = np.zeros(len(k0s)) -norm = np.zeros_like(normApp) -res = np.zeros_like(normApp) -err = np.zeros_like(normApp) -for j in range(len(k0s)): - normApp[j] = approx.normApprox(k0s[j]) - norm[j] = approx.normHF(k0s[j]) - res[j] = (approx.estimatorNormEngine.norm(approx.getRes(k0s[j])) - / approx.estimatorNormEngine.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(0)), - 1.25*np.max(norm)*np.ones(approx.mus.shape, dtype = float), 'rx') -plt.xlim([kl, kr]) -plt.grid() -plt.show() -plt.close() - -plt.figure() -plt.semilogy(k0s, res) -plt.semilogy(k0s, resApp, '--') -plt.semilogy(np.real(approx.mus(0)), - 4.*np.max(resApp)*np.ones(approx.mus.shape, dtype = float), 'rx') -plt.xlim([kl, kr]) -plt.grid() -plt.show() -plt.close() - -plt.figure() -plt.semilogy(k0s, err) -plt.xlim([kl, kr]) -plt.grid() -plt.show() -plt.close() - -polesApp = approx.getPoles() -mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) -print("Outliers:", polesApp[mask]) -polesApp = polesApp[~mask] -plt.figure() -plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') -plt.axis('equal') -plt.grid() -plt.show() -plt.close() diff --git a/examples/greedy/squareBubbleHomog.py b/examples/greedy/squareBubbleHomog.py deleted file mode 100644 index dbe5516..0000000 --- a/examples/greedy/squareBubbleHomog.py +++ /dev/null @@ -1,117 +0,0 @@ -import numpy as np -from rrompy.hfengines.linear_problem import \ - HelmholtzSquareBubbleProblemEngine as HSBPE -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 -from rrompy.solver.fenics import L2NormMatrix - -verb = 2 -timed = False -algo = "Pade" -#algo = "RB" -polyBasis = "LEGENDRE" -#polyBasis = "CHEBYSHEV" -#polyBasis = "MONOMIAL" -errorEstimatorKind = "BARE" -#errorEstimatorKind = "BASIC" -#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, 'S':10, 'polybasis':polyBasis, - 'errorEstimatorKind':errorEstimatorKind, 'interactive':False} - -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: - params.pop('Delta') - params.pop('polybasis') - params.pop('errorEstimatorKind') - approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) -approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) - -if timed: - from time import clock - start_time = clock() - approx.greedy() - print("Time: ", clock() - start_time) -else: - approx.greedy(True) - -approx.samplingEngine.verbosity = 0 -approx.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.estimatorNormEngine.norm(approx.getRes(k0s[j])) - / approx.estimatorNormEngine.norm(approx.getRHS(k0s[j]))) - err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) -resApp = approx.errorEstimator(k0s) - -plt.figure() -plt.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(0)), - 4.*np.max(norm)*np.ones(approx.mus.shape, dtype = float), 'rx') -plt.xlim([kl, kr]) -plt.grid() -plt.show() -plt.close() - -plt.figure() -plt.semilogy(k0s, res) -plt.semilogy(k0s, resApp, '--') -plt.semilogy(polesexact, - 2.*np.max(resApp)*np.ones_like(polesexact, dtype = float), 'm.') -plt.semilogy(np.real(approx.mus(0)), - 4.*np.max(resApp)*np.ones(approx.mus.shape, dtype = float), 'rx') -plt.xlim([kl, kr]) -plt.grid() -plt.show() -plt.close() - -plt.figure() -plt.semilogy(k0s, err) -plt.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 deleted file mode 100644 index a32cb08..0000000 --- a/examples/greedy/squareScatteringHomog.py +++ /dev/null @@ -1,104 +0,0 @@ -import numpy as np -from rrompy.hfengines.linear_problem import \ - HelmholtzCavityScatteringProblemEngine as HCSPE -from rrompy.reduction_methods.distributed_greedy import \ - RationalInterpolantGreedy as Pade -from rrompy.reduction_methods.distributed_greedy import \ - RBDistributedGreedy as RB -from rrompy.solver.fenics import L2NormMatrix - -verb = 2 -timed = False -algo = "Pade" -algo = "RB" -polyBasis = "LEGENDRE" -polyBasis = "CHEBYSHEV" -#polyBasis = "MONOMIAL" -errorEstimatorKind = "BARE" -errorEstimatorKind = "BASIC" -#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':5e-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) -approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) - -if timed: - from time import clock - start_time = clock() - approx.greedy() - print("Time: ", clock() - start_time) -else: - approx.greedy(True) - -approx.samplingEngine.verbosity = 0 -approx.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.estimatorNormEngine.norm(approx.getRes(k0s[j])) - / approx.estimatorNormEngine.norm(approx.getRHS(k0s[j]))) - err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) -resApp = approx.errorEstimator(k0s) - -plt.figure() -plt.plot(k0s, norm) -plt.plot(k0s, normApp, '--') -plt.plot(np.real(approx.mus(0)), - 1.25*np.max(norm)*np.ones(approx.mus.shape, dtype = float), 'rx') -plt.xlim([kl, kr]) -plt.grid() -plt.show() -plt.close() - -plt.figure() -plt.semilogy(k0s, res) -plt.semilogy(k0s, resApp, '--') -plt.semilogy(np.real(approx.mus(0)), - 4.*np.max(resApp)*np.ones(approx.mus.shape, dtype = float), 'rx') -plt.xlim([kl, kr]) -plt.grid() -plt.show() -plt.close() - -plt.figure() -plt.semilogy(k0s, err) -plt.xlim([kl, kr]) -plt.grid() -plt.show() -plt.close() - -polesApp = approx.getPoles() -mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) -print("Outliers:", polesApp[mask]) -polesApp = polesApp[~mask] -plt.figure() -plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') -plt.axis('equal') -plt.grid() -plt.show() -plt.close() diff --git a/examples/greedy/squareTransmissionNonHomog.py b/examples/greedy/squareTransmissionNonHomog.py deleted file mode 100644 index f729c20..0000000 --- a/examples/greedy/squareTransmissionNonHomog.py +++ /dev/null @@ -1,116 +0,0 @@ -import numpy as np -from rrompy.hfengines.linear_problem import \ - HelmholtzSquareTransmissionProblemEngine as HSTPE -from rrompy.reduction_methods.distributed_greedy import \ - RationalInterpolantGreedy as Pade -from rrompy.reduction_methods.distributed_greedy import \ - RBDistributedGreedy as RB -from rrompy.solver.fenics import L2NormMatrix - -timed = False -verb = 2 -algo = "Pade" -#algo = "RB" -polyBasis = "LEGENDRE" -#polyBasis = "CHEBYSHEV" -#polyBasis = "MONOMIAL" -homog = True -#homog = False -errorEstimatorKind = "BARE" -errorEstimatorKind = "BASIC" -#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, 'S':5, 'polybasis':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: - params.pop('Delta') - params.pop('polybasis') - params.pop('errorEstimatorKind') - approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb, - homogeneized = homog) -approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) - -if timed: - from time import clock - start_time = clock() - approx.greedy() - print("Time: ", clock() - start_time) -else: - approx.greedy(True) - -approx.samplingEngine.verbosity = 0 -approx.verbosity = 0 -from matplotlib import pyplot as plt -normApp = np.zeros(len(k0s)) -norm = np.zeros_like(normApp) -res = np.zeros_like(normApp) -err = np.zeros_like(normApp) -for j in range(len(k0s)): - normApp[j] = approx.normApprox(k0s[j]) - norm[j] = approx.normHF(k0s[j]) - res[j] = (approx.estimatorNormEngine.norm( - approx.getRes(k0s[j], homogeneized=homog)) - / approx.estimatorNormEngine.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(0)), - 4.*np.max(norm)*np.ones(approx.mus.shape, 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(0)), - 4.*np.max(resApp)*np.ones(approx.mus.shape, 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/PolesCentered.py b/examples/pod/PolesCentered.py index aee0e2e..02ca328 100644 --- a/examples/pod/PolesCentered.py +++ b/examples/pod/PolesCentered.py @@ -1,68 +1,70 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleProblemEngine as HSBPE -from rrompy.reduction_methods.centered import RationalPade as Pade -from rrompy.reduction_methods.centered import RBCentered as RB -from rrompy.utilities.base import squareResonances +from rrompy.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB +from rrompy.utilities.numerical import squareResonances +from rrompy.parameter.parameter_sampling import ManualSampler as MS verb = 0 k0 = (12+0.j) ** .5 +krange = [[9. ** .5], [15. ** .5]] Nmin, Nmax = 2, 10 Nvals = np.arange(Nmin, Nmax + 1, 2) -params = {'N':Nmin, 'M':0, 'S':Nmin + 1, 'POD':True}#, 'robustTol':1e-14} +params = {'N':Nmin, 'M':0, 'S':Nmin + 1, 'POD':True, + 'sampler': MS(krange, points = [k0], scalingExp = 2.)} #boolCon = lambda x : np.abs(np.imag(x)) < 1e-1 * np.abs(np.real(x) # - np.real(z0)) #cleanupParameters = {'boolCondition':boolCon, 'residueCheck':True} solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 25, verbosity = verb) solver.omega = np.real(k0) -approxP = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb)#, -# equilibration = True, cleanupParameters = cleanupParameters) +approxP = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) approxR = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) rP, rE = [None] * len(Nvals), [None] * len(Nvals) verbose = 1 for j, N in enumerate(Nvals): if verbose > 0: print('N = E = {}'.format(N)) approxP.approxParameters = {'N':N, 'S':N+1} approxR.approxParameters = {'R':N, 'S':N+1} if verbose > 1: print(approxP.approxParameters) print(approxR.approxParameters) rP[j] = approxP.getPoles() rE[j] = approxR.getPoles() if verbose > 2: print(rP) print(rE) from matplotlib import pyplot as plt plotRows = int(np.ceil(len(Nvals) / 3)) fig, axes = plt.subplots(plotRows, 3, figsize = (15, 3.5 * plotRows)) for j, N in enumerate(Nvals): i1, i2 = int(np.floor(j / 3)), j % 3 axes[i1, i2].set_title('N = E = {}'.format(N)) axes[i1, i2].plot(np.real(rP[j]), np.imag(rP[j]), 'Xb', label="Pade'", markersize = 8) axes[i1, i2].plot(np.real(rE[j]), np.imag(rE[j]), 'Pr', label="RB", markersize = 8) axes[i1, i2].axhline(linewidth=1, color='k') xmin, xmax = axes[i1, i2].get_xlim() height = (xmax - xmin) / 2. res = np.power(squareResonances(xmin**2., xmax**2., False), .5) axes[i1, i2].plot(res, np.zeros_like(res), 'ok', markersize = 4) axes[i1, i2].plot(np.real(k0), np.imag(k0), 'om', markersize = 5) axes[i1, i2].plot(np.real(k0) * np.ones(2), 1.5 * height * np.arange(-1, 3, 2), '--m') axes[i1, i2].grid() axes[i1, i2].set_xlim(xmin, xmax) axes[i1, i2].set_ylim(- height, height) p = axes[i1, i2].legend() plt.tight_layout() for j in range((len(Nvals) - 1) % 3 + 1, 3): axes[plotRows - 1, j].axis('off') diff --git a/examples/pod/PolesDistributed.py b/examples/pod/PolesDistributed.py index ee0987b..6dd37aa 100644 --- a/examples/pod/PolesDistributed.py +++ b/examples/pod/PolesDistributed.py @@ -1,48 +1,45 @@ from matplotlib import pyplot as plt import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleProblemEngine as HSBPE -from rrompy.reduction_methods.distributed import RationalInterpolant as RI +from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.parameter.parameter_sampling import QuadratureSampler as QS -from rrompy.utilities.base import squareResonances +from rrompy.utilities.numerical import squareResonances verb = 0 ks = [1., 46. ** .5] solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 20, verbosity = verb) k0 = np.mean(np.power(ks, 2.)) ** .5 k0 = 3.46104724 solver.omega = np.real(k0) -rescaling = lambda x: np.power(x, 2.) -rescalingInv = lambda x: np.power(x, .5) - nsets = 12 paramsPade = {'S':2, 'POD':True, 'polybasis':"LEGENDRE", - 'sampler':QS(ks, "UNIFORM", rescaling, rescalingInv)} + 'sampler':QS(ks, "UNIFORM", rescaleExp = 2.)} approx = RI(solver, mu0 = k0, approxParameters = paramsPade, verbosity = verb) poles = [None] * nsets samples = [None] * nsets polesexact = np.unique(np.power( squareResonances(ks[0]**2., ks[1]**2., False), .5)) for i in range(1, nsets + 1): print("N = {}".format(4 * i)) approx.approxParameters = {'N': 4 * i, 'M': 4 * i, 'S': 4 * i + 1} approx.setupApprox() poles[i - 1] = approx.getPoles() samples[i - 1] = (approx.mus ** 2.).data for i in range(1, nsets + 1): plt.figure() plt.plot(np.real(poles[i - 1]), np.imag(poles[i - 1]), 'kx') plt.plot(polesexact, np.zeros_like(polesexact), 'm.') plt.plot(np.real(samples[i - 1]), np.imag(samples[i - 1]), 'r*') plt.xlim(ks) plt.ylim((ks[0] - ks[1]) / 2., (ks[1] - ks[0]) / 2.) plt.title("N = {}, Neff = {}".format(4 * i, len(poles[i - 1]))) plt.grid() plt.show() plt.close() diff --git a/examples/pod/RBCentered.py b/examples/pod/RBCentered.py deleted file mode 100644 index d3bb17a..0000000 --- a/examples/pod/RBCentered.py +++ /dev/null @@ -1,100 +0,0 @@ -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.centered import RBCentered as RB - -testNo = 3 -verb = 100 -homog = True -#homog = False -loadName = "RBCenteredModel.pkl" - -if testNo in [1, -1]: - if testNo > 0: - params = {'S':5, 'R':4, '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, approxParameters = {'S':1}, - verbosity = verb) - approx.loadTrainedModel(loadName) - - approx.plotApprox(ktar, name = 'u_RB') - approx.plotHF(ktar, name = 'u_HF') - approx.plotErr(ktar, name = 'err') - approx.plotRes(ktar, name = 'res') - - appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) - resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) - print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, - np.divide(appErr, solNorm))) - print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, - np.divide(resNorm, RHSNorm))) - - if testNo > 0: - approx.storeTrainedModel("RBCenteredModel", forceNewFile = False) - print(approx.trainedModel.data.__dict__) - -############ -elif testNo == 2: - params = {'S':8, 'R':7, '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 == 3: - params = {'S':10, 'POD':True} - k0 = 3. - ktar = 4. + 0.j - - solver = HBSPE(R = 5, 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.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/RationalHermiteInterpolant.py b/examples/pod/RationalHermiteInterpolant.py index abbed8f..d62aed4 100644 --- a/examples/pod/RationalHermiteInterpolant.py +++ b/examples/pod/RationalHermiteInterpolant.py @@ -1,139 +1,133 @@ 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.distributed import RationalInterpolant as RI +from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, ManualSampler as MS) testNo = 3 verb = 100 polyBasis = "CHEBYSHEV" #polyBasis = "LEGENDRE" #polyBasis = "MONOMIAL" rep = "REPEAT" #rep = "TILE" homog = True #homog = False if testNo == 1: k0s = np.power([10 + 0.j, 14 + 0.j], .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 - rescaling = lambda x: np.power(x, 2.) - rescalingInv = lambda x: np.power(x, .5) - samplerBase = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv) + samplerBase = QS(k0s, "CHEBYSHEV", 2.) S = 8 if rep == "REPEAT": points = np.repeat(samplerBase.generatePoints(2).data, int(np.ceil(S / 2))) else: # if rep == "TILE": points = np.tile(samplerBase.generatePoints(2).data, int(np.ceil(S / 2))) params = {'N':7, 'M':6, 'S':S, 'POD':True, 'polybasis':polyBasis, - 'sampler':MS(k0s, points = points, scaling = rescaling, - scalingInv = rescalingInv)} + 'sampler':MS(k0s, points = points, 2.)} ktar = (11 + .5j) ** .5 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, verbosity = verb) solver.omega = np.real(k0) approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) ############ elif testNo == 2: k0s = [3.85 + 0.j, 4.15 + 0.j] k0 = np.mean(k0s) ktar = 4 + 0.j - rescaling = lambda x: np.power(x, 2.) - rescalingInv = lambda x: np.power(x, .5) - samplerBase = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv) + samplerBase = QS(k0s, "CHEBYSHEV", 2.) S = 10 if rep == "REPEAT": points = np.repeat(samplerBase.generatePoints(5).data, int(np.ceil(S / 5))) else: # if rep == "TILE": points = np.tile(samplerBase.generatePoints(5).data, int(np.ceil(S / 5))) params = {'N':8, 'M':9, 'S':S, 'POD':True, 'polybasis':polyBasis, - 'sampler':MS(k0s, points = points, rescale = rescaling, - rescaleInv = rescalingInv)} + 'sampler':MS(k0s, points = points, 2.)} 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 - 0.j samplerBase = QS(k0s, "CHEBYSHEV") S = 15 if rep == "REPEAT": points = np.repeat(samplerBase.generatePoints(5).data, int(np.ceil(S / 5))) else: # if rep == "TILE": points = np.tile(samplerBase.generatePoints(5).data, int(np.ceil(S / 5))) params = {'N':14, 'M':14, 'S':S, 'POD':True, 'polybasis':polyBasis, 'sampler':MS(k0s, points = points)} solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40, verbosity = verb) 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/RationalInterpolant.py b/examples/pod/RationalInterpolant.py index 397ee94..51f7069 100644 --- a/examples/pod/RationalInterpolant.py +++ b/examples/pod/RationalInterpolant.py @@ -1,125 +1,121 @@ 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.distributed import RationalInterpolant as RI +from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.parameter.parameter_sampling import QuadratureSampler as QS testNo = 3 verb = 100 polyBasis = "CHEBYSHEV" polyBasis = "LEGENDRE" #polyBasis = "MONOMIAL" homog = True #homog = False loadName = "RationalInterpolantModel.pkl" if testNo in [1, -1]: if testNo > 0: k0s = np.power([10 + 0.j, 14 + 0.j], .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 - 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, - 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)} + 'sampler':QS(k0s, "CHEBYSHEV", 2.)} ktar = (11 + .5j) ** .5 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, verbosity = verb) if testNo > 0: solver.omega = np.real(k0) approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() else: approx = RI(solver, mu0 = 0, approxParameters = {'S':1, 'muBounds':[0, 1]}, verbosity = verb) approx.loadTrainedModel(loadName) approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) if testNo > 0: approx.storeTrainedModel("RationalInterpolantModel", forceNewFile = False) print(approx.trainedModel.data.__dict__) ############ elif testNo == 2: k0s = [3.85 + 0.j, 4.15 + 0.j] k0 = np.mean(k0s) ktar = 4 + 0.j - 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, - 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)} + 'sampler':QS(k0s, "CHEBYSHEV", 2.)} 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 - 0.j params = {'N':10, 'M':9, 'S':15, 'POD':True, 'polybasis':polyBasis, 'sampler':QS(k0s, "CHEBYSHEV")} solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40, verbosity = verb) 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/RationalPade.py b/examples/pod/RationalPade.py deleted file mode 100644 index 8d9e1a5..0000000 --- a/examples/pod/RationalPade.py +++ /dev/null @@ -1,106 +0,0 @@ -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.centered import RationalPade as Pade - -testNo = 3 -verb = 100 -homog = True -#homog = False -loadName = "RationalPadeModel.pkl" - -if testNo in [1, -1]: - if testNo > 0: - params = {'N':4, 'M':3, 'S':5, '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, approxParameters = {'S':1}, - verbosity = verb) - approx.loadTrainedModel(loadName) - - approx.plotApprox(ktar, name = 'u_Pade''') - approx.plotHF(ktar, name = 'u_HF') - approx.plotErr(ktar, name = 'err') - approx.plotRes(ktar, name = 'res') - - appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) - resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) - print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, - np.divide(appErr, solNorm))) - print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, - np.divide(resNorm, RHSNorm))) - print('\nPoles Pade'':') - print(approx.getPoles()) - - if testNo > 0: - approx.storeTrainedModel("RationalPadeModel", forceNewFile = False) - print(approx.trainedModel.data.__dict__) - -############ -elif testNo == 2: - params = {'N':6, 'M':7, 'S':8, '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 == 3: - params = {'M':9, 'N':9, 'S':10, 'POD':True} - k0 = 3. - ktar = 4. + 0.j - - solver = HBSPE(R = 5, 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.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/RBDistributed.py b/examples/pod/ReducedBasis.py similarity index 88% rename from examples/pod/RBDistributed.py rename to examples/pod/ReducedBasis.py index 606cfe2..f48f6bd 100644 --- a/examples/pod/RBDistributed.py +++ b/examples/pod/ReducedBasis.py @@ -1,112 +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.distributed import RBDistributed as RB +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS testNo = 1 verb = 100 homog = True #homog = False -loadName = "RBDistributedModel.pkl" +loadName = "ReducedBasisModel.pkl" if testNo in [1, -1]: if testNo > 0: k0s = np.power([10 + 0.j, 14 + 0.j], .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 - rescaling = lambda x: np.power(x, 2.) - rescalingInv = lambda x: np.power(x, .5) params = {'S':5, 'R':4, 'POD':True, - 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)} + 'sampler':QS(k0s, "CHEBYSHEV", 2.)} ktar = (11 + .5j) ** .5 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, verbosity = verb) if testNo > 0: approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() else: approx = RB(solver, mu0 = 0, approxParameters = {'S':1, 'muBounds':[0, 1]}, verbosity = verb) approx.loadTrainedModel(loadName) approx.plotApprox(ktar, name = 'u_RB') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) if testNo > 0: - approx.storeTrainedModel("RBDistributedModel", forceNewFile = False) + approx.storeTrainedModel("ReducedBasisModel", forceNewFile = False) print(approx.trainedModel.data.__dict__) ############ elif testNo == 2: k0s = [3.85 + 0.j, 4.15 + 0.j] k0 = np.mean(k0s) ktar = 4 + .15j - rescaling = lambda x: np.power(x, 2.) - rescalingInv = lambda x: np.power(x, .5) params = {'S':10, 'R':9, 'POD':True, - 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)} + 'sampler':QS(k0s, "CHEBYSHEV", 2.)} 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.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_RB') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) ############ elif testNo == 3: k0s = [2., 5.] k0 = np.mean(k0s) ktar = 4.5 - 0.j params = {'S':15, 'R':10, 'POD':True, 'sampler':QS(k0s, "CHEBYSHEV")} solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40, verbosity = verb) 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/laplaceGaussianCentered.py b/examples/pod/laplaceGaussianCentered.py deleted file mode 100644 index 924d282..0000000 --- a/examples/pod/laplaceGaussianCentered.py +++ /dev/null @@ -1,47 +0,0 @@ -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 RB -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)[0] - solver.plotmesh() - print(solver.norm(uh)) - solver.plot(uh) - -############ -if testNo == 2: - params = {'N':8, 'M':8, 'S':9, '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, ['S', '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/matrix_pod.py b/examples/pod/matrix_pod.py index 99e79d8..8fbd742 100644 --- a/examples/pod/matrix_pod.py +++ b/examples/pod/matrix_pod.py @@ -1,80 +1,68 @@ 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.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.reduction_methods.standard import RationalInterpolant as RI +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS test = 2 -method = "RationalPade" -#method = "RationalInterpolant" -#method = "RBCentered" -#method = "RBDistributed" +method = "RationalInterpolant" +#method = "ReducedBasis" verb = 0 N = 100 solver = MEB(verbosity = verb) solver.npar = 1 solver.nAs = 2 if test == 1: solver.As = [sp.spdiags([np.arange(1, 1 + N)], [0], N, N), - sp.eye(N)] elif test == 2: solver.setSolver("SOLVE") fftB = np.fft.fft(np.eye(N)) * N**-.5 solver.As = [fftB.dot(np.multiply(np.arange(1, 1 + N), fftB.conj()).T), - np.eye(N)] np.random.seed(420) solver.nbs = 1 solver.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] mu0 = 10.25 mutar = 12.5 murange = [5.25, 15.25] -if method == "RationalPade": - params = {'N':10, 'M':9, 'S':11, 'POD':True} - approx = RP(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) -elif method == "RationalInterpolant": +if method == "RationalInterpolant": params = {'N':10, 'M':9, 'S':11, 'POD':True, 'polybasis':"CHEBYSHEV", 'sampler':QS(murange, "CHEBYSHEV")} approx = RI(solver, mu0 = mu0, approxParameters = params, verbosity = verb) -elif method == "RBCentered": - params = {'R':10, 'S':11, 'POD':True} - approx = RBC(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) -elif method == "RBDistributed": +elif method == "ReducedBasis": params = {'R':10, 'S':11, 'POD':True, 'sampler':QS(murange, "CHEBYSHEV")} - approx = RBD(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) + approx = RB(solver, mu0 = mu0, approxParameters = params, + verbosity = verb) approx.setupApprox() approx.plotApprox(mutar, name = 'u_app') approx.plotHF(mutar, name = 'u_HF') approx.plotErr(mutar, name = 'err') approx.plotRes(mutar, name = 'res') appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar) resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) polesTrue = np.arange(1, 1 + N) polesTrue = polesTrue[polesTrue >= murange[0]] polesTrue = polesTrue[polesTrue <= murange[1]] polesApp = approx.getPoles() mask = (np.real(polesApp) < murange[0]) | (np.real(polesApp) > murange[1]) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.plot(polesTrue, np.zeros_like(polesTrue), 'm.') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/pod/parametricDomain.py b/examples/pod/parametricDomain.py deleted file mode 100644 index 8f3c124..0000000 --- a/examples/pod/parametricDomain.py +++ /dev/null @@ -1,52 +0,0 @@ -import numpy as np -from rrompy.hfengines.linear_problem import \ - HelmholtzSquareBubbleDomainProblemEngine as HSBDPE -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 = 2 -verb = 0 - -if testNo == 1: - mu = 7 ** .5 - solver = HSBDPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, mu0 = mu, - degree_threshold = 10, verbosity = verb) - - uh = solver.solve(mu)[0] - solver.plotmesh() - print(solver.norm(uh)) - solver.plot(uh) - -############ -if testNo == 2: - params = {'M':8, 'N':8, 'S':9, 'POD':True} - mu0 = 7 ** .5 - mutar = 6.75 ** .5 - solver = HSBDPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, mu0 = mu0, - degree_threshold = 10, verbosity = verb) - approxP = Pade(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) - paramsRB = subdict(params, ['S', '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()) - diff --git a/examples/pod/scatteringSquare.py b/examples/pod/scatteringSquare.py index 2656d80..163f778 100644 --- a/examples/pod/scatteringSquare.py +++ b/examples/pod/scatteringSquare.py @@ -1,85 +1,72 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzCavityScatteringProblemEngine as CSPE -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.reduction_methods.standard import RationalInterpolant as PD +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS from operator import itemgetter def subdict(d, ks): return dict(zip(ks, itemgetter(*ks)(d))) verb = 0 #################### test = "solve" -#test = "Centered" -#test = "Distributed" +#test = "approx" plotSamples = True k0 = 10 kLeft, kRight = 9, 11 ktar = 9.5 ktars = np.linspace(8.5, 11.5, 125) #ktars = np.array([k0]) kappa = 5 n = 50 solver = CSPE(kappa = kappa, n = n, verbosity = verb) solver.omega = k0 if test == "solve": uh = solver.solve(k0)[0] print(solver.norm(uh)) solver.plot(uh, what = ['ABS', 'REAL']) -elif test in ["Centered", "Distributed"]: - if test == "Centered": - params = {'N':8, 'M':7, 'R':8, 'S':9, 'POD':True} - parPade = subdict(params, ['N', 'M', 'S', 'POD']) - parRB = subdict(params, ['R', 'S', 'POD']) - approxPade = PC(solver, mu0 = k0, approxParameters = parPade, - verbosity = verb) - approxRB = RBC(solver, mu0 = k0, approxParameters = parRB, - verbosity = verb) - else: - params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, - 'polybasis':"CHEBYSHEV", - 'sampler':QS([kLeft, kRight], "CHEBYSHEV")} - parPade = subdict(params, ['N', 'M', 'S', 'POD', 'polybasis', - 'sampler']) - parRB = subdict(params, ['R', 'S', 'POD', 'sampler']) - approxPade = PD(solver, mu0 = np.mean([kLeft, kRight]), - approxParameters = parPade, - verbosity = verb) - approxRB = RBD(solver, mu0 = np.mean([kLeft, kRight]), - approxParameters = parRB, - verbosity = verb) +elif test == "approx": + params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, + 'polybasis':"CHEBYSHEV", + 'sampler':QS([kLeft, kRight], "CHEBYSHEV")} + parPade = subdict(params, ['N', 'M', 'S', 'POD', 'polybasis', + 'sampler']) + parRB = subdict(params, ['R', 'S', 'POD', 'sampler']) + approxPade = PD(solver, mu0 = np.mean([kLeft, kRight]), + approxParameters = parPade, + verbosity = verb) + approxRB = RB(solver, mu0 = np.mean([kLeft, kRight]), + approxParameters = parRB, verbosity = verb) approxPade.setupApprox() approxRB.setupApprox() if plotSamples: approxPade.plotSamples() PadeErr, solNorm = approxPade.normErr(ktar), approxPade.normHF(ktar) RBErr = approxRB.normErr(ktar) print(('SolNorm:\t{}\nErrPade:\t{}\nErrRelPade:\t{}\nErrRB:\t\t{}' '\nErrRelRB:\t{}').format(solNorm, PadeErr, np.divide(PadeErr, solNorm), RBErr, np.divide(RBErr, solNorm))) print('\nPoles Pade'':') print(approxPade.getPoles()) print('\nPoles RB:') print(approxRB.getPoles()) approxPade.plotHF(ktar, name = 'u_ex') approxPade.plotApprox(ktar, name = 'u_Pade''') approxRB.plotApprox(ktar, name = 'u_RB') approxPade.plotErr(ktar, name = 'errPade''') approxRB.plotErr(ktar, name = 'errRB') diff --git a/rrompy/hfengines/base/__init__.py b/rrompy/hfengines/base/__init__.py index 5046f4a..78d84fc 100644 --- a/rrompy/hfengines/base/__init__.py +++ b/rrompy/hfengines/base/__init__.py @@ -1,32 +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 .matrix_engine_base import MatrixEngineBase +from .marginal_proxy_engine import MarginalProxyEngine from .problem_engine_base import ProblemEngineBase from .vector_problem_engine_base import VectorProblemEngineBase from .boundary_conditions import BoundaryConditions __all__ = [ 'MatrixEngineBase', + 'MarginalProxyEngine', 'ProblemEngineBase', 'VectorProblemEngineBase', 'BoundaryConditions' ] diff --git a/rrompy/hfengines/base/marginal_proxy_engine.py b/rrompy/hfengines/base/marginal_proxy_engine.py new file mode 100644 index 0000000..f4fc5ae --- /dev/null +++ b/rrompy/hfengines/base/marginal_proxy_engine.py @@ -0,0 +1,387 @@ +# 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 copy import deepcopy as copy, copy as softcopy +from rrompy.utilities.base.types import (Np1D, Np2D, ScOp, List, ListAny, + paramVal, paramList, HFEng, sampList) +from rrompy.utilities.base import freepar as fp +from rrompy.utilities.numerical import multibinom, marginalizePolyList +from rrompy.utilities.poly_fitting.polynomial import ( + hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) +from rrompy.utilities.exception_manager import RROMPyAssert +from rrompy.parameter import checkParameter, checkParameterList +from rrompy.sampling import sampleList, emptySampleList +from rrompy.solver.scipy import tensorizeLS, detensorizeLS + + +__all__ = ['MarginalProxyEngine'] + +class MarginalProxyEngine: + """ + Marginalized should prescribe fixed value for the marginalized parameters + and leave freepar/None elsewhere. + """ + + def __init__(self, HFEngine:HFEng, marginalized:Np1D): + self.baseHF = HFEngine + self.marg = marginalized + self._setupAs() + self._setupbs() + + @property + def freeLocations(self): + return tuple([x for x in range(self.nparBase) if self.marg[x] == fp]) + + @property + def fixedLocations(self): + return tuple([x for x in range(self.nparBase) if self.marg[x] != fp]) + + @property + def muFixed(self): + muF = checkParameter([m for m in self.marg if m != fp]) + if self.nparBase - self.npar > 0: muF = muF[0] + return muF + + @property + def rescalingExp(self): + return [self.baseHF.rescalingExp[x] for x in self.freeLocations] + + @property + def rescalingExpFixed(self): + return [self.baseHF.rescalingExp[x] for x in self.fixedLocations] + + @property + def V(self): + return self.baseHF.V + + def name(self) -> str: + return "{}-proxy for {}".format(self.freeLocations, self.baseHF.name()) + + def __str__(self) -> str: + return self.name() + + def __repr__(self) -> str: + return self.__str__() + " at " + hex(id(self)) + + def __dir_base__(self): + return [x for x in self.__dir__() if x[:2] != "__"] + + def __deepcopy__(self, memo): + return softcopy(self) + + @property + def npar(self): + """Value of npar.""" + return len(self.freeLocations) + + @property + def nparBase(self): + """Value of npar.""" + return self.baseHF.npar + + @property + def nAs(self): + """Value of nAs.""" + return self._nAs + + @property + def nbs(self): + """Value of nbs.""" + return self._nbs + + @property + def nbsH(self) -> int: + return max(self.nbs, self.nAs) + + def spacedim(self): + return self.As[0].shape[1] + + def checkParameter(self, mu:paramList): + return checkParameter(mu, self.npar) + + def checkParameterList(self, mu:paramList): + return checkParameterList(mu, self.npar) + + def buildEnergyNormForm(self): + self.baseHF.buildEnergyNormForm() + self.energyNormMatrix = self.baseHF.energyNormMatrix + + def buildEnergyNormDualForm(self): + self.baseHF.buildEnergyNormDualForm() + self.energyNormDualMatrix = self.baseHF.energyNormDualMatrix + + def buildDualityPairingForm(self): + self.baseHF.buildDualityPairingForm() + self.dualityMatrix = self.baseHF.dualityMatrix + + def buildEnergyNormPartialDualForm(self): + self.baseHF.buildEnergyNormPartialDualForm() + self.energyNormPartialDualMatrix = ( + self.baseHF.energyNormPartialDualMatrix) + + def innerProduct(self, u:Np2D, v:Np2D, onlyDiag : bool = False, + dual : bool = False, duality : bool = True) -> Np2D: + return self.baseHF.innerProduct(u, v, onlyDiag, dual, duality) + + def norm(self, u:Np2D, dual : bool = False, duality : bool = True) -> Np1D: + return self.baseHF.norm(u, dual, duality) + + def checkAInBounds(self, derI : int = 0): + """Check if derivative index is oob for operator of linear system.""" + if derI < 0 or derI >= self.nAs: + d = self.spacedim() + return scsp.csr_matrix((np.zeros(0), np.zeros(0), np.zeros(d + 1)), + shape = (d, d), dtype = np.complex) + + def checkbInBounds(self, derI : int = 0, homogeneized : bool = False): + """Check if derivative index is oob for RHS of linear system.""" + nbs = self.nbsH if homogeneized else self.nbs + if derI < 0 or derI >= nbs: + return np.zeros(self.spacedim(), dtype = np.complex) + + def _assembleA(self, mu : paramVal = [], der : List[int] = 0, + derI : int = None) -> ScOp: + """Assemble (derivative of) operator of linear system.""" + mu = self.checkParameter(mu) + if self.npar > 0: mu = mu[0] + if not hasattr(der, "__len__"): der = [der] * self.npar + if derI is None: derI = hashD(der) + Anull = self.checkAInBounds(derI) + if Anull is not None: return Anull + rExp = self.rescalingExp + A = copy(self.As[derI]) + for j in range(derI + 1, self.nAs): + derIdx = hashI(j, self.npar) + diffIdx = [x - y for (x, y) in zip(derIdx, der)] + if np.all([x >= 0 for x in diffIdx]): + A = A + (np.prod((mu ** rExp) ** diffIdx) + * multibinom(derIdx, diffIdx) * self.As[j]) + return A + + def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: + """ + Assemble terms of operator of linear system and return it (or its + derivative) at a given parameter. + """ + mu = self.checkParameter(mu) + if not hasattr(der, "__len__"): der = [der] * self.npar + derI = hashD(der) + return self._assembleA(mu, der, derI) + + def _setupAs(self): + mVals = [fp] * self.nparBase + muFixedEff = self.muFixed ** self.rescalingExpFixed + for i, mFE in zip(self.fixedLocations, muFixedEff): + mVals[i] = mFE + self.As = marginalizePolyList(self.baseHF.As, mVals, "auto") + self._nAs = len(self.As) + + def affineLinearSystemA(self, mu : paramVal = [], + sclF : ListAny = None) -> List[Np2D]: + """ + Assemble affine blocks of operator of linear system (just linear + blocks). + """ + if sclF is None: sclF = [1.] * self.npar + As = [None] * self.nAs + for j in range(self.nAs): + derIdx = hashI(j, self.npar) + mult = np.prod([s ** d for (s, d) in zip(sclF, derIdx)]) + As[j] = mult * self.A(mu, derIdx) + return As + + def _assembleb(self, mu : paramVal = [], der : List[int] = 0, + derI : int = None, homogeneized : bool = False) -> ScOp: + """Assemble (derivative of) (homogeneized) RHS of linear system.""" + mu = self.checkParameter(mu) + if self.npar > 0: mu = mu[0] + if not hasattr(der, "__len__"): der = [der] * self.npar + if derI is None: derI = hashD(der) + bnull = self.checkbInBounds(derI, homogeneized) + if bnull is not None: return bnull + bs = self.bsH if homogeneized else self.bs + rExp = self.rescalingExp + b = copy(bs[derI]) + for j in range(derI + 1, len(bs)): + derIdx = hashI(j, self.npar) + diffIdx = [x - y for (x, y) in zip(derIdx, der)] + if np.all([x >= 0 for x in diffIdx]): + b = b + (np.prod((mu ** rExp) ** diffIdx) + * multibinom(derIdx, diffIdx) * bs[j]) + return b + + def b(self, mu : paramVal = [], der : List[int] = 0, + homogeneized : bool = False) -> Np1D: + """ + Assemble terms of (homogeneized) RHS of linear system and return it (or + its derivative) at a given parameter. + """ + mu = self.checkParameter(mu) + if not hasattr(der, "__len__"): der = [der] * self.npar + derI = hashD(der) + return self._assembleb(mu, der, derI, homogeneized) + + def _setupbs(self): + mVals = [fp] * self.nparBase + muFixedEff = self.muFixed ** self.rescalingExpFixed + for i, mFE in zip(self.fixedLocations, muFixedEff): + mVals[i] = mFE + self.bs = marginalizePolyList(self.baseHF.bs, mVals, "auto") + self._nbs = len(self.bs) + + def affineLinearSystemb(self, mu : paramVal = [], sclF : ListAny = None, + homogeneized : bool = False) -> List[Np1D]: + """ + Assemble affine blocks of RHS of linear system (just linear blocks). + """ + if sclF is None: sclF = [1.] * self.npar + nbs = self.nbsH if homogeneized else self.nbs + bs = [None] * nbs + for j in range(nbs): + derIdx = hashI(j, self.npar) + mult = np.prod([s ** d for (s, d) in zip(sclF, derIdx)]) + bs[j] = mult * self.b(mu, derIdx, homogeneized) + return bs + + def solve(self, mu : paramList = [], RHS : sampList = None, + homogeneized : bool = False) -> sampList: + """ + Find solution of linear system. + + Args: + mu: parameter value. + RHS: RHS of linear system. If None, defaults to that of parametric + system. Defaults to None. + """ + mu = self.checkParameterList(mu)[0] + if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype) + sol = emptySampleList() + if len(mu) > 0: + if RHS is None: + RHS = [self.b(m, homogeneized = homogeneized) for m in mu] + RHS = sampleList(RHS) + mult = 0 if len(RHS) == 1 else 1 + RROMPyAssert(mult * (len(mu) - 1) + 1, len(RHS), "Sample size") + u = self.baseHF._solver(self.A(mu[0]), RHS[0], + self.baseHF._solverArgs) + sol.reset((len(u), len(mu)), dtype = u.dtype) + sol[0] = u + for j in range(1, len(mu), self.baseHF._solveBatchSize): + if self.baseHF._solveBatchSize != 1: + iRange = list(range(j, min(j + self.baseHF._solveBatchSize, + len(mu)))) + As = [self.A(mu[i]) for i in iRange] + bs = [RHS[mult * i] for i in iRange] + A, b = tensorizeLS(As, bs) + else: + A, b = self.A(mu[j]), RHS[mult * j] + solStack = self.baseHF._solver(A, b, self.baseHF._solverArgs) + if self.baseHF._solveBatchSize != 1: + sol[iRange] = detensorizeLS(solStack, len(iRange)) + else: + sol[j] = solStack + return sol + + def residual(self, u:sampList, mu : paramList = [], + homogeneized : bool = False, + duality : bool = True) -> sampList: + """ + Find residual of linear system for given approximate solution. + + Args: + u: numpy complex array with function dofs. If None, set to 0. + mu: parameter value. + """ + mu = self.checkParameterList(mu)[0] + if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype) + if u is not None: + u = sampleList(u) + mult = 0 if len(u) == 1 else 1 + RROMPyAssert(mult * (len(mu) - 1) + 1, len(u), "Sample size") + res = emptySampleList() + if duality and not hasattr(self, "dualityMatrix"): + self.buildDualityPairingForm() + for j in range(len(mu)): + b = self.b(mu[j], homogeneized = homogeneized) + if u is None: + r = b + else: + r = b - self.A(mu[j]).dot(u[mult * j]) + if j == 0: + res.reset((len(r), len(mu)), dtype = r.dtype) + if duality: + r = self.dualityMatrix.dot(r) + res[j] = r + return res + + def _rayleighQuotient(self, *args, **kwargs) -> float: + return self.baseHF._rayleighQuotient(*args, **kwargs) + + def stabilityFactor(self, u:sampList, mu : paramList = [], + nIterP : int = 10, nIterR : int = 10) -> sampList: + """ + Find stability factor of matrix of linear system using iterative + inverse power iteration- and Rayleigh quotient-based procedure. + + Args: + u: numpy complex arrays with function dofs. + mu: parameter values. + nIterP: number of iterations of power method. + nIterR: number of iterations of Rayleigh quotient method. + """ + mu = self.checkParameterList(mu)[0] + if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype) + u = sampleList(u) + mult = 0 if len(u) == 1 else 1 + RROMPyAssert(mult * (len(mu) - 1) + 1, len(u), "Sample size") + stabFact = np.empty(len(mu), dtype = float) + if not hasattr(self, "energyNormMatrix"): + self.buildEnergyNormForm() + for j in range(len(mu)): + stabFact[j] = self._rayleighQuotient(self.A(mu[j]), u[mult * j], + self.energyNormMatrix, + 0., nIterP, nIterR) + return stabFact + + def plot(self, *args, **kwargs): + """ + Do some nice plots of the complex-valued function with given dofs. + + """ + self.baseHF.plot(*args, **kwargs) + + def plotmesh(self, *args, **kwargs): + """ + Do a nice plot of the mesh. + """ + self.baseHF.plotmesh(*args, **kwargs) + + def outParaview(self, *args, **kwargs): + """ + Output complex-valued function with given dofs to ParaView file. + """ + self.baseHF.outParaview(*args, **kwargs) + + def outParaviewTimeDomain(self, *args, **kwargs): + """ + Output complex-valued function with given dofs to ParaView file, + converted to time domain. + """ + self.baseHF.outParaviewTimeDomain(*args, **kwargs) diff --git a/rrompy/hfengines/base/matrix_engine_base.py b/rrompy/hfengines/base/matrix_engine_base.py index d4dc0c7..5effc6c 100644 --- a/rrompy/hfengines/base/matrix_engine_base.py +++ b/rrompy/hfengines/base/matrix_engine_base.py @@ -1,568 +1,517 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from abc import abstractmethod import numpy as np import scipy.sparse as scsp from matplotlib import pyplot as plt from copy import deepcopy as copy, copy as softcopy from rrompy.utilities.base.types import (Np1D, Np2D, ScOp, strLst, Tuple, List, - DictAny, paramVal, paramList, + ListAny, DictAny, paramVal, paramList, sampList) -from rrompy.utilities.base import (purgeList, getNewFilename, verbosityDepth, - multibinom) +from rrompy.utilities.base import (purgeList, getNewFilename, + verbosityManager as vbMng) +from rrompy.utilities.numerical import multibinom from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert from rrompy.parameter import checkParameter, checkParameterList from rrompy.sampling import sampleList, emptySampleList from rrompy.solver import setupSolver, Np2DLikeEye from rrompy.solver.scipy import tensorizeLS, detensorizeLS __all__ = ['MatrixEngineBase'] class MatrixEngineBase: """ Generic solver for parametric matrix problems. Attributes: verbosity: Verbosity level. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. bsH: Numpy array representation of homogeneized bs. energyNormMatrix: Scipy sparse matrix representing inner product. energyNormDualMatrix: Scipy sparse matrix representing dual inner product. dualityMatrix: Scipy sparse matrix representing duality. energyNormPartialDualMatrix: Scipy sparse matrix representing dual inner product without duality. """ _solveBatchSize = 1 def __init__(self, verbosity : int = 10, timestamp : bool = True): self.verbosity = verbosity self.timestamp = timestamp self.nAs, self.nbs = 1, 1 self.setSolver("SPSOLVE", {"use_umfpack" : False}) self.npar = 0 def name(self) -> str: return self.__class__.__name__ def __str__(self) -> str: return self.name() def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) def __dir_base__(self): return [x for x in self.__dir__() if x[:2] != "__"] def __deepcopy__(self, memo): return softcopy(self) @property def npar(self): """Value of npar.""" return self._npar @npar.setter def npar(self, npar): nparOld = self._npar if hasattr(self, "_npar") else -1 if npar != nparOld: self.rescalingExp = [1.] * npar self._npar = npar @property def nAs(self): """Value of nAs.""" return self._nAs @nAs.setter def nAs(self, nAs): self._nAs = nAs self.resetAs() @property def nbs(self): """Value of nbs.""" return self._nbs @nbs.setter def nbs(self, nbs): self._nbs = nbs self.resetbs() @property def nbsH(self) -> int: return max(self.nbs, self.nAs) def spacedim(self): return self.As[0].shape[1] def checkParameter(self, mu:paramList): return checkParameter(mu, self.npar) def checkParameterList(self, mu:paramList): return checkParameterList(mu, self.npar) def buildEnergyNormForm(self): # eye """ Build sparse matrix (in CSR format) representative of scalar product. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling energy matrix.", 20) self.energyNormMatrix = Np2DLikeEye() - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy matrix.", 20) def buildEnergyNormDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product. """ if not hasattr(self, "energyNormMatrix"): self.buildEnergyNormForm() - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy dual matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling energy dual matrix.", 20) self.energyNormDualMatrix = self.energyNormMatrix - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy dual matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy dual matrix.", 20) def buildDualityPairingForm(self): """Build sparse matrix (in CSR format) representative of duality.""" if not hasattr(self, "energyNormMatrix"): self.buildEnergyNormForm() - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling duality matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling duality matrix.", 20) self.dualityMatrix = self.energyNormMatrix - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling duality matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling duality matrix.", 20) def buildEnergyNormPartialDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product without duality. """ if not hasattr(self, "energyNormDualMatrix"): self.buildEnergyNormDualForm() - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy partial dual matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling energy partial dual matrix.", 20) self.energyNormPartialDualMatrix = self.energyNormDualMatrix - if self.verbosity >= 20: - verbosityDepth("DEL", - "Done assembling energy partial dual matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy partial dual matrix.", 20) def innerProduct(self, u:Np2D, v:Np2D, onlyDiag : bool = False, dual : bool = False, duality : bool = True) -> Np2D: """Scalar product.""" if dual: if duality: if not hasattr(self, "energyNormDualMatrix"): self.buildEnergyNormDualForm() energyMat = self.energyNormDualMatrix else: if not hasattr(self, "energyNormPartialDualMatrix"): self.buildEnergyNormPartialDualForm() energyMat = self.energyNormPartialDualMatrix else: if not hasattr(self, "energyNormMatrix"): self.buildEnergyNormForm() energyMat = self.energyNormMatrix if not isinstance(u, (np.ndarray,)): u = u.data if not isinstance(v, (np.ndarray,)): v = v.data if onlyDiag: return np.sum(energyMat.dot(u) * v.conj(), axis = 0) return v.T.conj().dot(energyMat.dot(u)) def norm(self, u:Np2D, dual : bool = False, duality : bool = True) -> Np1D: return np.abs(self.innerProduct(u, u, onlyDiag = True, dual = dual, duality = duality)) ** .5 def checkAInBounds(self, derI : int = 0): """Check if derivative index is oob for operator of linear system.""" if derI < 0 or derI >= self.nAs: d = self.spacedim() return scsp.csr_matrix((np.zeros(0), np.zeros(0), np.zeros(d + 1)), shape = (d, d), dtype = np.complex) def checkbInBounds(self, derI : int = 0, homogeneized : bool = False): """Check if derivative index is oob for RHS of linear system.""" nbs = self.nbsH if homogeneized else self.nbs if derI < 0 or derI >= nbs: return np.zeros(self.spacedim(), dtype = np.complex) def resetAs(self): """Reset (derivatives of) operator of linear system.""" self.setAs([None] * self.nAs) if hasattr(self, "_nbs"): self.resetbsH() def resetbs(self): """Reset (derivatives of) RHS of linear system.""" self.setbs([None] * self.nbs) if hasattr(self, "_nAs"): self.resetbsH() def resetbsH(self): """Reset (derivatives of) homogeneized RHS of linear system.""" self.setbsH([None] * self.nbsH) def setAs(self, As:List[Np2D]): """Assign terms of operator of linear system.""" if len(As) != self.nAs: raise RROMPyException(("Expected number {} of terms of As not " "matching given list length {}.").format(self.nAs, len(As))) self.As = [copy(A) for A in As] def setbs(self, bs:List[Np1D]): """Assign terms of RHS of linear system.""" if len(bs) != self.nbs: raise RROMPyException(("Expected number {} of terms of bs not " "matching given list length {}.").format(self.nbs, len(bs))) self.bs = [copy(b) for b in bs] def setbsH(self, bsH:List[Np1D]): """Assign terms of homogeneized RHS of linear system.""" if len(bsH) != self.nbsH: raise RROMPyException(("Expected number {} of terms of bsH not " "matching given list length {}.").format(self.nbsH, len(bsH))) self.bsH = [copy(bH) for bH in bsH] def _assembleA(self, mu : paramVal = [], der : List[int] = 0, - derI : int = None, muBase : paramVal = None) -> ScOp: + derI : int = None) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) - if muBase is None: muBase = [0.] * self.npar - muBase = self.checkParameter(muBase) - if self.npar > 0: mu, muBase = mu[0], muBase[0] + if self.npar > 0: mu = mu[0] if not hasattr(der, "__len__"): der = [der] * self.npar if derI is None: derI = hashD(der) Anull = self.checkAInBounds(derI) if Anull is not None: return Anull rExp = self.rescalingExp A = copy(self.As[derI]) for j in range(derI + 1, self.nAs): derIdx = hashI(j, self.npar) diffIdx = [x - y for (x, y) in zip(derIdx, der)] if np.all([x >= 0 for x in diffIdx]): - A = A + (np.prod((mu ** rExp - muBase ** rExp) ** diffIdx) + A = A + (np.prod((mu ** rExp) ** diffIdx) * multibinom(derIdx, diffIdx) * self.As[j]) return A @abstractmethod def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """ Assemble terms of operator of linear system and return it (or its derivative) at a given parameter. """ if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) for j in range(derI, self.nAs): if self.As[j] is None: self.As[j] = self.checkAInBounds(-1) return self._assembleA(mu, der, derI) - def affineLinearSystemA(self, mu : paramVal = []) -> List[Np2D]: + def affineLinearSystemA(self, mu : paramVal = [], + sclF : ListAny = None) -> List[Np2D]: """ Assemble affine blocks of operator of linear system (just linear blocks). """ + if sclF is None: sclF = [1.] * self.npar As = [None] * self.nAs for j in range(self.nAs): - As[j] = self.A(mu, hashI(j, self.npar)) + derIdx = hashI(j, self.npar) + mult = np.prod([s ** d for (s, d) in zip(sclF, derIdx)]) + As[j] = mult * self.A(mu, derIdx) return As - def affineWeightsA(self, mu : paramVal = []) -> List[str]: - """ - Assemble affine blocks of operator of linear system (just affine - weights). Stored as strings for the sake of pickling. - """ - mu = self.checkParameter(mu) - lambdasA = ["1."] - mu0Eff = mu ** self.rescalingExp - for j in range(1, self.nAs): - lambdasA += ["np.prod((mu ** ({1}) - [{0}]) ** ({2}))".format( - ','.join([str(x) for x in mu0Eff[0]]), - self.rescalingExp, hashI(j, self.npar))] - return lambdasA - - def affineBlocksA(self, mu : paramVal = [])\ - -> Tuple[List[Np2D], List[str]]: - """Assemble affine blocks of operator of linear system.""" - return self.affineLinearSystemA(mu), self.affineWeightsA(mu) - def _assembleb(self, mu : paramVal = [], der : List[int] = 0, - derI : int = None, homogeneized : bool = False, - muBase : paramVal = None) -> ScOp: + derI : int = None, homogeneized : bool = False) -> ScOp: """Assemble (derivative of) (homogeneized) RHS of linear system.""" mu = self.checkParameter(mu) - if muBase is None: muBase = [0.] * self.npar - muBase = self.checkParameter(muBase) - if self.npar > 0: mu, muBase = mu[0], muBase[0] + if self.npar > 0: mu = mu[0] if not hasattr(der, "__len__"): der = [der] * self.npar if derI is None: derI = hashD(der) bnull = self.checkbInBounds(derI, homogeneized) if bnull is not None: return bnull bs = self.bsH if homogeneized else self.bs rExp = self.rescalingExp b = copy(bs[derI]) for j in range(derI + 1, len(bs)): derIdx = hashI(j, self.npar) diffIdx = [x - y for (x, y) in zip(derIdx, der)] if np.all([x >= 0 for x in diffIdx]): - b = b + (np.prod((mu ** rExp - muBase ** rExp) ** diffIdx) + b = b + (np.prod((mu ** rExp) ** diffIdx) * multibinom(derIdx, diffIdx) * bs[j]) return b @abstractmethod def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """ Assemble terms of (homogeneized) RHS of linear system and return it (or its derivative) at a given parameter. """ if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) if homogeneized: for j in range(derI, self.nbsH): if self.bsH[j] is None: self.bsH[j] = self.checkbInBounds(-1) else: for j in range(derI, self.nbs): if self.bs[j] is None: self.bs[j] = self.checkbInBounds(-1) return self._assembleb(mu, der, derI, homogeneized) - def affineLinearSystemb(self, mu : paramVal = [], + def affineLinearSystemb(self, mu : paramVal = [], sclF : ListAny = None, homogeneized : bool = False) -> List[Np1D]: """ Assemble affine blocks of RHS of linear system (just linear blocks). """ + if sclF is None: sclF = [1.] * self.npar nbs = self.nbsH if homogeneized else self.nbs bs = [None] * nbs for j in range(nbs): - bs[j] = self.b(mu, hashI(j, self.npar), homogeneized) + derIdx = hashI(j, self.npar) + mult = np.prod([s ** d for (s, d) in zip(sclF, derIdx)]) + bs[j] = mult * self.b(mu, derIdx, homogeneized) return bs - def affineWeightsb(self, mu : paramVal = [], - homogeneized : bool = False) -> List[str]: - """ - Assemble affine blocks of RHS of linear system (just affine weights). - Stored as strings for the sake of pickling. - """ - mu = self.checkParameter(mu) - nbs = self.nbsH if homogeneized else self.nbs - lambdasb = ["1."] - mu0Eff = mu ** self.rescalingExp - for j in range(1, nbs): - lambdasb += ["np.prod((mu ** ({1}) - [{0}]) ** ({2}))".format( - ','.join([str(x) for x in mu0Eff[0]]), - self.rescalingExp, hashI(j, self.npar))] - return lambdasb - - def affineBlocksb(self, mu : paramVal = [], homogeneized : bool = False)\ - -> Tuple[List[Np1D], List[str]]: - """Assemble affine blocks of RHS of linear system.""" - return (self.affineLinearSystemb(mu, homogeneized), - self.affineWeightsb(mu, homogeneized)) - def setSolver(self, solverType:str, solverArgs : DictAny = {}): """Choose solver type and parameters.""" self._solver, self._solverArgs = setupSolver(solverType, solverArgs) def solve(self, mu : paramList = [], RHS : sampList = None, homogeneized : bool = False) -> sampList: """ Find solution of linear system. Args: mu: parameter value. RHS: RHS of linear system. If None, defaults to that of parametric system. Defaults to None. """ - mu, _ = self.checkParameterList(mu) + mu = self.checkParameterList(mu)[0] if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype) sol = emptySampleList() if len(mu) > 0: if RHS is None: RHS = [self.b(m, homogeneized = homogeneized) for m in mu] RHS = sampleList(RHS) mult = 0 if len(RHS) == 1 else 1 RROMPyAssert(mult * (len(mu) - 1) + 1, len(RHS), "Sample size") u = self._solver(self.A(mu[0]), RHS[0], self._solverArgs) sol.reset((len(u), len(mu)), dtype = u.dtype) sol[0] = u for j in range(1, len(mu), self._solveBatchSize): if self._solveBatchSize != 1: iRange = list(range(j, min(j + self._solveBatchSize, len(mu)))) As = [self.A(mu[i]) for i in iRange] bs = [RHS[mult * i] for i in iRange] A, b = tensorizeLS(As, bs) else: A, b = self.A(mu[j]), RHS[mult * j] solStack = self._solver(A, b, self._solverArgs) if self._solveBatchSize != 1: sol[iRange] = detensorizeLS(solStack, len(iRange)) else: sol[j] = solStack return sol def residual(self, u:sampList, mu : paramList = [], homogeneized : bool = False, duality : bool = True) -> sampList: """ Find residual of linear system for given approximate solution. Args: u: numpy complex array with function dofs. If None, set to 0. mu: parameter value. """ - mu, _ = self.checkParameterList(mu) + mu = self.checkParameterList(mu)[0] if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype) if u is not None: u = sampleList(u) mult = 0 if len(u) == 1 else 1 RROMPyAssert(mult * (len(mu) - 1) + 1, len(u), "Sample size") res = emptySampleList() if duality and not hasattr(self, "dualityMatrix"): self.buildDualityPairingForm() for j in range(len(mu)): b = self.b(mu[j], homogeneized = homogeneized) if u is None: r = b else: r = b - self.A(mu[j]).dot(u[mult * j]) if j == 0: res.reset((len(r), len(mu)), dtype = r.dtype) if duality: r = self.dualityMatrix.dot(r) res[j] = r return res def _rayleighQuotient(self, A:Np2D, v0:Np1D, M:Np2D, sigma : float = 0., nIterP : int = 10, nIterR : int = 10) -> float: nIterP = min(nIterP, len(v0) // 2) nIterR = min(nIterR, (len(v0) + 1) // 2) v0 /= v0.T.conj().dot(M.dot(v0)) ** .5 for j in range(nIterP): v0 = self._solver(A - sigma * M, M.dot(v0), self._solverArgs) v0 /= v0.T.conj().dot(M.dot(v0)) ** .5 l0 = v0.T.conj().dot(A.dot(v0)) for j in range(nIterR): v0 = self._solver(A - l0 * M, M.dot(v0), self._solverArgs) v0 /= v0.T.conj().dot(M.dot(v0)) ** .5 l0 = v0.T.conj().dot(A.dot(v0)) if np.isnan(l0): l0 = np.finfo(float).eps return np.abs(l0) def stabilityFactor(self, u:sampList, mu : paramList = [], nIterP : int = 10, nIterR : int = 10) -> sampList: """ Find stability factor of matrix of linear system using iterative inverse power iteration- and Rayleigh quotient-based procedure. Args: u: numpy complex arrays with function dofs. mu: parameter values. nIterP: number of iterations of power method. nIterR: number of iterations of Rayleigh quotient method. """ - mu, _ = self.checkParameterList(mu) + mu = self.checkParameterList(mu)[0] if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype) u = sampleList(u) mult = 0 if len(u) == 1 else 1 RROMPyAssert(mult * (len(mu) - 1) + 1, len(u), "Sample size") stabFact = np.empty(len(mu), dtype = float) if not hasattr(self, "energyNormMatrix"): self.buildEnergyNormForm() for j in range(len(mu)): stabFact[j] = self._rayleighQuotient(self.A(mu[j]), u[mult * j], self.energyNormMatrix, 0., nIterP, nIterR) return stabFact - def plot(self, u:Np1D, name : str = "u", save : str = None, - what : strLst = 'all', saveFormat : str = "eps", - saveDPI : int = 100, show : bool = True, **figspecs): + def plot(self, u:Np1D, warping : List[callable] = None, name : str = "u", + save : str = None, what : strLst = 'all', + saveFormat : str = "eps", saveDPI : int = 100, show : bool = True, + pyplotArgs : dict = {}, **figspecs): """ Do some nice plots of the complex-valued function with given dofs. Args: u: numpy complex array with function dofs. name(optional): Name to be shown as title of the plots. Defaults to 'u'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. what(optional): Which plots to do. If list, can contain 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. show(optional): Whether to show figure. Defaults to True. + pyplotArgs(optional): Optional arguments for pyplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ if isinstance(what, (str,)): if what.upper() == 'ALL': what = ['ABS', 'PHASE', 'REAL', 'IMAG'] else: what = [what] what = purgeList(what, ['ABS', 'PHASE', 'REAL', 'IMAG'], listname = self.name() + ".what", baselevel = 1) if len(what) == 0: return if 'figsize' not in figspecs.keys(): figspecs['figsize'] = (13. * len(what) / 4, 3) subplotcode = 100 + len(what) * 10 idxs = np.arange(self.spacedim()) + if warping is not None: + idxs = warping[0](np.arange(self.spacedim())) plt.figure(**figspecs) plt.jet() if 'ABS' in what: subplotcode = subplotcode + 1 plt.subplot(subplotcode) - plt.plot(idxs, np.abs(u).flatten()) + plt.plot(idxs, np.abs(u).flatten(), **pyplotArgs) plt.title("|{0}|".format(name)) if 'PHASE' in what: subplotcode = subplotcode + 1 plt.subplot(subplotcode) - plt.plot(idxs, np.angle(u).flatten()) + plt.plot(idxs, np.angle(u).flatten(), **pyplotArgs) plt.title("phase({0})".format(name)) if 'REAL' in what: subplotcode = subplotcode + 1 plt.subplot(subplotcode) - plt.plot(idxs, np.real(u).flatten()) + plt.plot(idxs, np.real(u).flatten(), **pyplotArgs) plt.title("Re({0})".format(name)) if 'IMAG' in what: subplotcode = subplotcode + 1 plt.subplot(subplotcode) - plt.plot(idxs, np.imag(u).flatten()) + plt.plot(idxs, np.imag(u).flatten(), **pyplotArgs) plt.title("Im({0})".format(name)) if save is not None: save = save.strip() plt.savefig(getNewFilename("{}_fig_".format(save), saveFormat), format = saveFormat, dpi = saveDPI) if show: plt.show() plt.close() diff --git a/rrompy/hfengines/base/problem_engine_base.py b/rrompy/hfengines/base/problem_engine_base.py index d0c61c0..5144951 100644 --- a/rrompy/hfengines/base/problem_engine_base.py +++ b/rrompy/hfengines/base/problem_engine_base.py @@ -1,367 +1,366 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from os import path, mkdir import fenics as fen import numpy as np from matplotlib import pyplot as plt from copy import deepcopy as copy from rrompy.utilities.base.types import (Np1D, strLst, FenFunc, Tuple, List, paramVal) -from rrompy.utilities.base import purgeList, getNewFilename, verbosityDepth +from rrompy.utilities.base import (purgeList, getNewFilename, + verbosityManager as vbMng) from rrompy.solver import Np2DLikeEye from rrompy.solver.fenics import L2NormMatrix, fenplot, interp_project from .boundary_conditions import BoundaryConditions from .matrix_engine_base import MatrixEngineBase from rrompy.utilities.exception_manager import RROMPyException __all__ = ['ProblemEngineBase'] class ProblemEngineBase(MatrixEngineBase): """ Generic solver for parametric problems. Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real FE space. u: Generic trial functions for variational form evaluation. v: Generic test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. energyNormDualMatrix: Scipy sparse matrix representing inner product over V'. dualityMatrix: Scipy sparse matrix representing duality V-V'. energyNormPartialDualMatrix: Scipy sparse matrix representing dual inner product between Riesz representers V-V. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. """ _dualityCompress = None def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(verbosity = verbosity, timestamp = timestamp) self.BCManager = BoundaryConditions("Dirichlet") self.V = fen.FunctionSpace(fen.UnitSquareMesh(10, 10), "P", 1) self.mu0BC = np.nan self.degree_threshold = degree_threshold self.npar = 0 @property def V(self): """Value of V.""" return self._V @V.setter def V(self, V): self.resetAs() self.resetbs() if not type(V).__name__ == 'FunctionSpace': raise RROMPyException("V type not recognized.") self._V = V self.u = fen.TrialFunction(V) self.v = fen.TestFunction(V) def spacedim(self): return self.V.dim() def buildEnergyNormForm(self): """ Build sparse matrix (in CSR format) representative of scalar product. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling energy matrix.", 20) self.energyNormMatrix = L2NormMatrix(self.V) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy matrix.", 20) def buildDualityPairingForm(self): """Build sparse matrix (in CSR format) representative of duality.""" - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling duality matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling duality matrix.", 20) self.dualityMatrix = Np2DLikeEye() - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling duality matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling duality matrix.", 20) def liftDirichletData(self, mu : paramVal = []) -> Np1D: """Lift Dirichlet datum.""" mu = self.checkParameter(mu) if mu != self.mu0BC: self.mu0BC = copy(mu) liftRe = interp_project(self.DirichletDatum[0], self.V) liftIm = interp_project(self.DirichletDatum[1], self.V) self.liftedDirichletDatum = (np.array(liftRe.vector()) + 1.j * np.array(liftIm.vector())) return self.liftedDirichletDatum def reduceQuadratureDegree(self, fun:FenFunc, name:str): """Check whether to reduce compiler parameters to degree threshold.""" if not np.isinf(self.degree_threshold): from ufl.algorithms.estimate_degrees import ( estimate_total_polynomial_degree as ETPD) try: deg = ETPD(fun) except: return False if deg > self.degree_threshold: - if self.verbosity >= 15: - verbosityDepth("MAIN", ("Reducing quadrature degree from " - "{} to {} for {}.").format( - deg, - self.degree_threshold, - name), - timestamp = self.timestamp) + vbMng(self, "MAIN", + ("Reducing quadrature degree from {} to {} for " + "{}.").format(deg, self.degree_threshold, name), 15) return True return False def iterReduceQuadratureDegree(self, funsNames:List[Tuple[FenFunc, str]]): """ Iterate reduceQuadratureDegree over list and define reduce compiler parameters. """ if funsNames is not None: for fun, name in funsNames: if self.reduceQuadratureDegree(fun, name): return {"quadrature_degree" : self.degree_threshold} return {} def plot(self, u:Np1D, warping : List[callable] = None, name : str = "u", save : str = None, what : strLst = 'all', saveFormat : str = "eps", saveDPI : int = 100, show : bool = True, - **figspecs): + fenplotArgs : dict = {}, **figspecs): """ Do some nice plots of the complex-valued function with given dofs. Args: u: numpy complex array with function dofs. warping(optional): Domain warping functions. name(optional): Name to be shown as title of the plots. Defaults to 'u'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. what(optional): Which plots to do. If list, can contain 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. show(optional): Whether to show figure. Defaults to True. + fenplotArgs(optional): Optional arguments for fenplot. 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 = fenplot(uAb, warping = warping, title = "|{0}|".format(name)) - plt.colorbar(p) + p = fenplot(uAb, warping = warping, title = "|{0}|".format(name), + **fenplotArgs) + if self.V.mesh().geometric_dimension() > 1: + plt.colorbar(p) if 'PHASE' in what: uPh = fen.Function(self.V) uPh.vector().set_local(np.angle(u)) subplotcode = subplotcode + 1 plt.subplot(subplotcode) p = fenplot(uPh, warping = warping, - title = "phase({0})".format(name)) - plt.colorbar(p) + title = "phase({0})".format(name), **fenplotArgs) + if self.V.mesh().geometric_dimension() > 1: + plt.colorbar(p) if 'REAL' in what: uRe = fen.Function(self.V) uRe.vector().set_local(np.real(u)) subplotcode = subplotcode + 1 plt.subplot(subplotcode) - p = fenplot(uRe, warping = warping, title = "Re({0})".format(name)) - plt.colorbar(p) + p = fenplot(uRe, warping = warping, title = "Re({0})".format(name), + **fenplotArgs) + if self.V.mesh().geometric_dimension() > 1: + plt.colorbar(p) if 'IMAG' in what: uIm = fen.Function(self.V) uIm.vector().set_local(np.imag(u)) subplotcode = subplotcode + 1 plt.subplot(subplotcode) - p = fenplot(uIm, warping = warping, title = "Im({0})".format(name)) - plt.colorbar(p) + p = fenplot(uIm, warping = warping, title = "Im({0})".format(name), + **fenplotArgs) + if self.V.mesh().geometric_dimension() > 1: + plt.colorbar(p) if save is not None: save = save.strip() plt.savefig(getNewFilename("{}_fig_".format(save), saveFormat), format = saveFormat, dpi = saveDPI) if show: plt.show() plt.close() def plotmesh(self, warping : List[callable] = None, name : str = "Mesh", save : str = None, saveFormat : str = "eps", - saveDPI : int = 100, show : bool = True, **figspecs): + saveDPI : int = 100, show : bool = True, + fenplotArgs : dict = {}, **figspecs): """ Do a nice plot of the mesh. Args: u: numpy complex array with function dofs. warping(optional): Domain warping functions. name(optional): Name to be shown as title of the plots. Defaults to 'u'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. show(optional): Whether to show figure. Defaults to True. + fenplotArgs(optional): Optional arguments for fenplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ plt.figure(**figspecs) - fenplot(self.V.mesh(), warping = warping) + fenplot(self.V.mesh(), warping = warping, **fenplotArgs) if save is not None: save = save.strip() plt.savefig(getNewFilename("{}_msh_".format(save), saveFormat), format = saveFormat, dpi = saveDPI) if show: plt.show() plt.close() def outParaview(self, u:Np1D, warping : List[callable] = None, name : str = "u", filename : str = "out", time : float = 0., what : strLst = 'all', forceNewFile : bool = True, folder : bool = False, filePW = None): """ Output complex-valued function with given dofs to ParaView file. Args: u: numpy complex array with function dofs. warping(optional): Domain warping functions. name(optional): Base name to be used for data output. filename(optional): Name of output file. time(optional): Timestamp. what(optional): Which plots to do. If list, can contain 'MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. forceNewFile(optional): Whether to create new output file. folder(optional): Whether to create an additional folder layer. filePW(optional): Fenics File entity (for time series). """ if isinstance(what, (str,)): if what.upper() == 'ALL': what = ['MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'] else: what = [what] what = purgeList(what, ['MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'], listname = self.name() + ".what", baselevel = 1) if len(what) == 0: return if filePW is None: if folder: if not path.exists(filename + "/"): mkdir(filename) idxpath = filename.rfind("/") filename += "/" + filename[idxpath + 1 :] if forceNewFile: filePW = fen.File(getNewFilename(filename, "pvd")) else: filePW = fen.File("{}.pvd".format(filename)) if warping is not None: fen.ALE.move(self.V.mesh(), interp_project(warping[0], self.V.mesh())) if what == ['MESH']: filePW << (self.V.mesh(), time) if 'ABS' in what: uAb = fen.Function(self.V, name = "{}_ABS".format(name)) uAb.vector().set_local(np.abs(u)) filePW << (uAb, time) if 'PHASE' in what: uPh = fen.Function(self.V, name = "{}_PHASE".format(name)) uPh.vector().set_local(np.angle(u)) filePW << (uPh, time) if 'REAL' in what: uRe = fen.Function(self.V, name = "{}_REAL".format(name)) uRe.vector().set_local(np.real(u)) filePW << (uRe, time) if 'IMAG' in what: uIm = fen.Function(self.V, name = "{}_IMAG".format(name)) uIm.vector().set_local(np.imag(u)) filePW << (uIm, time) if warping is not None: fen.ALE.move(self.V.mesh(), interp_project(warping[1], self.V.mesh())) return filePW def outParaviewTimeDomain(self, u:Np1D, omega:float, warping : List[callable] = None, timeFinal : float = None, periodResolution : int = 20, name : str = "u", filename : str = "out", forceNewFile : bool = True, folder : bool = False): """ Output complex-valued function with given dofs to ParaView file, converted to time domain. Args: u: numpy complex array with function dofs. omega: frequency. warping(optional): Domain warping functions. timeFinal(optional): final time of simulation. periodResolution(optional): number of time steps per period. name(optional): Base name to be used for data output. filename(optional): Name of output file. forceNewFile(optional): Whether to create new output file. folder(optional): Whether to create an additional folder layer. """ if folder: if not path.exists(filename + "/"): mkdir(filename) idxpath = filename.rfind("/") filename += "/" + filename[idxpath + 1 :] if forceNewFile: filePW = fen.File(getNewFilename(filename, "pvd")) else: filePW = fen.File("{}.pvd".format(filename)) omega = np.abs(omega) t = 0. dt = 2. * np.pi / omega / periodResolution if timeFinal is None: timeFinal = 2. * np.pi / omega - dt if warping is not None: fen.ALE.move(self.V.mesh(), interp_project(warping[0], self.V.mesh())) for j in range(int(np.ceil(timeFinal / dt)) + 1): ut = fen.Function(self.V, name = name) ut.vector().set_local(np.real(u) * np.cos(omega * t) + np.imag(u) * np.sin(omega * t)) filePW << (ut, t) t += dt if warping is not None: fen.ALE.move(self.V.mesh(), interp_project(warping[1], self.V.mesh())) return filePW diff --git a/rrompy/hfengines/base/vector_problem_engine_base.py b/rrompy/hfengines/base/vector_problem_engine_base.py index 92c55da..3cdbbbc 100644 --- a/rrompy/hfengines/base/vector_problem_engine_base.py +++ b/rrompy/hfengines/base/vector_problem_engine_base.py @@ -1,211 +1,224 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import fenics as fen import numpy as np from matplotlib import pyplot as plt from rrompy.utilities.base.types import Np1D, List, strLst from rrompy.utilities.base import purgeList, getNewFilename from rrompy.solver.fenics import fenplot from .problem_engine_base import ProblemEngineBase __all__ = ['VectorProblemEngineBase'] class VectorProblemEngineBase(ProblemEngineBase): """ Generic solver for parametric vector problems. Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real FE space. u: Generic trial functions for variational form evaluation. v: Generic test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. energyNormDualMatrix: Scipy sparse matrix representing inner product over V'. dualityMatrix: Scipy sparse matrix representing duality V-V'. energyNormPartialDualMatrix: Scipy sparse matrix representing dual inner product between Riesz representers V-V. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. """ def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.V = fen.VectorFunctionSpace(fen.UnitSquareMesh(10, 10), "P", 1) self.npar = 0 def plot(self, u:Np1D, warping : List[callable] = None, name : str = "u", save : str = None, what : strLst = 'all', saveFormat : str = "eps", saveDPI : int = 100, show : bool = True, - **figspecs): + fenplotArgs : dict = {}, **figspecs): """ Do some nice plots of the complex-valued function with given dofs. Args: u: numpy complex array with function dofs. warping(optional): Domain warping functions. name(optional): Name to be shown as title of the plots. Defaults to 'u'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. what(optional): Which plots to do. If list, can contain 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to. show(optional): Whether to show figure. Defaults to True. + fenplotArgs(optional): Optional arguments for fenplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ if isinstance(what, (str,)): if what.upper() == 'ALL': what = ['ABS', 'PHASE', 'REAL', 'IMAG'] else: what = [what] what = purgeList(what, ['ABS', 'PHASE', 'REAL', 'IMAG'], listname = self.name() + ".what", baselevel = 1) if 'figsize' not in figspecs.keys(): figspecs['figsize'] = (13. * max(len(what), 1) / 4, 3) if len(what) > 0: for j in range(self.V.num_sub_spaces()): subplotcode = 100 + len(what) * 10 II = self.V.sub(j).dofmap().dofs() Vj = self.V.sub(j).collapse() plt.figure(**figspecs) plt.jet() if 'ABS' in what: uAb = fen.Function(Vj) uAb.vector().set_local(np.abs(u[II])) subplotcode = subplotcode + 1 plt.subplot(subplotcode) p = fenplot(uAb, warping = warping, - title = "|{}_comp{}|".format(name, j)) - plt.colorbar(p) + title = "|{}_comp{}|".format(name, j, + **fenplotArgs)) + if self.V.mesh().geometric_dimension() > 1: + plt.colorbar(p) if 'PHASE' in what: uPh = fen.Function(Vj) uPh.vector().set_local(np.angle(u[II])) subplotcode = subplotcode + 1 plt.subplot(subplotcode) p = fenplot(uPh, warping = warping, - title = "phase({}_comp{})".format(name, j)) - plt.colorbar(p) + title = "phase({}_comp{})".format(name, j), + **fenplotArgs) + if self.V.mesh().geometric_dimension() > 1: + plt.colorbar(p) if 'REAL' in what: uRe = fen.Function(Vj) uRe.vector().set_local(np.real(u[II])) subplotcode = subplotcode + 1 plt.subplot(subplotcode) p = fenplot(uRe, warping = warping, - title = "Re({}_comp{})".format(name, j)) - plt.colorbar(p) + title = "Re({}_comp{})".format(name, j), + **fenplotArgs) + if self.V.mesh().geometric_dimension() > 1: + plt.colorbar(p) if 'IMAG' in what: uIm = fen.Function(Vj) uIm.vector().set_local(np.imag(u[II])) subplotcode = subplotcode + 1 plt.subplot(subplotcode) p = fenplot(uIm, warping = warping, - title = "Im({}_comp{})".format(name, j)) - plt.colorbar(p) + title = "Im({}_comp{})".format(name, j), + **fenplotArgs) + if self.V.mesh().geometric_dimension() > 1: + plt.colorbar(p) if save is not None: save = save.strip() - plt.savefig(getNewFilename("{}_comp{}_fig_".format(save, j), + plt.savefig(getNewFilename("{}_comp{}_fig_".format(save,j), saveFormat), format = saveFormat, dpi = saveDPI) if show: plt.show() plt.close() try: if len(what) > 1: figspecs['figsize'] = (2. / len(what) * figspecs['figsize'][0], figspecs['figsize'][1]) elif len(what) == 0: figspecs['figsize'] = (2. * figspecs['figsize'][0], figspecs['figsize'][1]) if len(what) == 0 or 'ABS' in what or 'REAL' in what: uVRe = fen.Function(self.V) uVRe.vector().set_local(np.real(u)) plt.figure(**figspecs) plt.jet() p = fenplot(uVRe, warping = warping, title = "{}_Re".format(name), - mode = "displacement") - plt.colorbar(p) + mode = "displacement", **fenplotArgs) + if self.V.mesh().geometric_dimension() > 1: + plt.colorbar(p) if save is not None: save = save.strip() plt.savefig(getNewFilename("{}_disp_Re_fig_".format(save), saveFormat), format = saveFormat, dpi = saveDPI) plt.show() plt.close() if 'ABS' in what or 'IMAG' in what: uVIm = fen.Function(self.V) uVIm.vector().set_local(np.imag(u)) plt.figure(**figspecs) plt.jet() p = fenplot(uVIm, warping = warping, title = "{}_Im".format(name), - mode = "displacement") - plt.colorbar(p) + mode = "displacement", **fenplotArgs) + if self.V.mesh().geometric_dimension() > 1: + plt.colorbar(p) if save is not None: save = save.strip() plt.savefig(getNewFilename("{}_disp_Im_fig_".format(save, j), saveFormat), format = saveFormat, dpi = saveDPI) if show: plt.show() plt.close() except: pass def plotmesh(self, warping : List[callable] = None, name : str = "Mesh", save : str = None, saveFormat : str = "eps", - saveDPI : int = 100, show : bool = True, **figspecs): + saveDPI : int = 100, show : bool = True, + fenplotArgs : dict = {}, **figspecs): """ Do a nice plot of the mesh. Args: u: numpy complex array with function dofs. warping(optional): Domain warping functions. name(optional): Name to be shown as title of the plots. Defaults to 'u'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. show(optional): Whether to show figure. Defaults to True. + fenplotArgs(optional): Optional arguments for fenplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ plt.figure(**figspecs) - fenplot(self.V.mesh(), warping = warping) + fenplot(self.V.mesh(), warping = warping, **fenplotArgs) if save is not None: save = save.strip() plt.savefig(getNewFilename("{}_msh_".format(save), saveFormat), format = saveFormat, dpi = saveDPI) if show: plt.show() plt.close() diff --git a/rrompy/hfengines/linear_problem/bidimensional/cookie_engine_single.py b/rrompy/hfengines/linear_problem/bidimensional/cookie_engine_single.py index 49e4acd..d24e1ca 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/cookie_engine_single.py +++ b/rrompy/hfengines/linear_problem/bidimensional/cookie_engine_single.py @@ -1,137 +1,125 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen import ufl from rrompy.utilities.base.types import ScOp, List, paramVal from rrompy.solver.fenics import fenONE, fenZERO from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) from rrompy.solver.fenics import fenics2Sparse __all__ = ['CookieEngineSingle'] class CookieEngineSingle(HelmholtzProblemEngine): def __init__(self, kappa:float, theta:float, n:int, R : int = 1., L : int = 2., nX : int = 1, nY : int = 1, mu0 : paramVal = [12. ** .5, 1.], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs = 5 self.npar = 2 self.rescalingExp = [2., 2.] mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(L * nX, L * nY), n * nX, n * nY) self.V = fen.FunctionSpace(mesh, "P", 1) x, y = fen.SpatialCoordinate(mesh)[:] cxs = np.linspace(0, L * nX, 2 * nX + 1)[1::2] cys = np.linspace(0, L * nY, 2 * nY + 1)[1::2] self.cookieIn = fenZERO for cx in cxs: for cy in cys: self.cookieIn += ufl.conditional( ufl.le((x-cx)**2. + (y-cy)**2., R**2.), fenONE, fenZERO) self.cookieOut = fenONE - self.cookieIn c, s = np.cos(theta), np.sin(theta) self.forcingTerm = [fen.cos(kappa * (c * x + s * y)), fen.sin(kappa * (c * x + s * y))] def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) aRe, aIm = self.diffusivity hRe, hIm = self.RobinDatumH termNames = ["diffusivity", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip( [aRe, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [aIm, hIm], [x + "Imag" for x in termNames])) a0Re = (aRe * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hRe * fen.dot(self.u, self.v) * self.ds(1)) a0Im = (aIm * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hIm * fen.dot(self.u, self.v) * self.ds(1)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 1 and self.As[1] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a1Re = - n2Re * self.cookieOut * fen.dot(self.u, self.v) * fen.dx a1Im = - n2Im * self.cookieOut * fen.dot(self.u, self.v) * fen.dx self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 2 and self.As[2] is None: self.As[2] = self.checkAInBounds(-1) if derI <= 3 and self.As[3] is None: self.As[3] = self.checkAInBounds(-1) if derI <= 4 and self.As[4] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A4.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A4.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a4Re = - n2Re * self.cookieIn * fen.dot(self.u, self.v) * fen.dx a4Im = - n2Im * self.cookieIn * fen.dot(self.u, self.v) * fen.dx self.As[4] = (fenics2Sparse(a4Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a4Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) diff --git a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py b/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py index cf05c29..343e450 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py +++ b/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py @@ -1,203 +1,159 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np +from numpy.polynomial.polynomial import polyfit as fit import fenics as fen from rrompy.utilities.base.types import (ScOp, List, Tuple, paramVal, Np1D, FenExpr) -from rrompy.solver.fenics import fenZERO, fenONE +from rrompy.solver.fenics import fenZERO from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) from rrompy.solver.fenics import fenics2Sparse, fenics2Vector __all__ = ['HelmholtzSquareDomainProblemEngine'] class HelmholtzSquareDomainProblemEngine(HelmholtzProblemEngine): """ Solver for square Helmholtz problems with parametric laplacian. - \dxx u - mu_2**2 \dyy u - mu_1**2 * u = f(mu_2) in \Omega = [0,\pi]^2 u = 0 on \partial\Omega """ def __init__(self, kappa:float, theta:float, n:int, mu0 : paramVal = [12. ** .5, 1.], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs, self.nbs = 6, 11 * 12 // 2 self.npar = 2 self.rescalingExp = [2., 1.] + self._theta = theta + self._kappa = kappa pi = np.pi mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(pi, pi), 3 * n, 3 * n) self.V = fen.FunctionSpace(mesh, "P", 1) - - c, s = np.cos(theta), np.sin(theta) - x, y = fen.SpatialCoordinate(mesh)[:] - self.forcingTerm = [fen.cos(kappa * (c * x + s / self.mu0(0, 1) * y)), - fen.sin(kappa * (c * x + s / self.mu0(0, 1) * y))] - self.forcingTermDer = kappa * s * y - - def getExtraFactorB(self, mu : paramVal = [], - derI : int = 0) -> Tuple[FenExpr, FenExpr]: - """Compute extra expression in RHS.""" - mu = self.checkParameter(mu) - def getPowMinusj(x, power): - powR = x ** power - powI = fenZERO - if power % 2 == 1: - powR, powI = powI, powR - if power % 4 > 1: - powR, powI = - powR, - powI - return powR, powI - if self.verbosity >= 25: - verbosityDepth("INIT", ("Assembling auxiliary expression for " - "forcing term derivative."), - timestamp = self.timestamp) - if derI == 0: return fenONE, fenZERO - coeffs = np.zeros(derI + 1) - coeffs[1] = - 2. - for j in range(2, derI + 1): - coeffs[1 :] = (-2. / j * coeffs[1 :] - - (3 - (1 + 2 * np.arange(derI)) / j) * coeffs[: -1]) - for j in range(derI): - powR, powI = getPowMinusj(self.forcingTermDer, derI - j) - mupBase = coeffs[j + 1] * mu(0, 1) ** (- 3 * derI + 2 * j) - mupR, mupI = np.real(mupBase), np.imag(mupBase) - if j == 0: - exprR = mupR * powR - mupI * powI - exprI = mupI * powR + mupR * powI - else: - exprR += mupR * powR - mupI * powI - exprI += mupI * powR + mupR * powI - if self.verbosity >= 25: - verbosityDepth("DEL", "Done assembling auxiliary expression.", - timestamp = self.timestamp) - return exprR, exprI + def getForcingTerm(self, mu : paramVal = []) -> Tuple[FenExpr, FenExpr]: + """Compute forcing term.""" + vbMng(self, "INIT", ("Assembling base expression for forcing term " + "at {}.").format(mu), 25) + c, s = np.cos(self._theta), np.sin(self._theta) + x, y = fen.SpatialCoordinate(self.V.mesh())[:] + forcingTerm = [fen.cos(self._kappa * (c * x + s / mu * y)), + fen.sin(self._kappa * (c * x + s / mu * y))] + vbMng(self, "DEL", "Done assembling base expression.", 25) + return forcingTerm + def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() for j in range(2, 5): if derI <= j and self.As[j] is None: self.As[j] = self.checkAInBounds(-1) if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 1 and self.As[1] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a1Re = - n2Re * fen.dot(self.u, self.v) * fen.dx a1Im = - n2Im * fen.dot(self.u, self.v) * fen.dx self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 5 and self.As[5] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A5.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A5.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a5Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx self.As[5] = fenics2Sparse(a5Re, {}, DirichletBC0, 0) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) nbsTot = self.nbsH if homogeneized else self.nbs bs = self.bsH if homogeneized else self.bs if homogeneized and self.mu0 != self.mu0BC: self.liftDirichletData(self.mu0) + bDEIMCoeffs = None for j in range(derI, nbsTot): derH = hashI(j, self.npar) if bs[j] is None: if np.sum(derH) != derH[-1]: if homogeneized: self.bsH[j] = self.checkbInBounds(-1) + Ader = self.A(0, derH) + self.bsH[j] -= Ader.dot(self.liftedDirichletDatum) else: self.bs[j] = self.checkbInBounds(-1) continue - if self.verbosity >= 20: - verbosityDepth("INIT", ("Assembling forcing term " - "b{}.").format(j), - timestamp = self.timestamp) - if j == 0: - u0Re, u0Im = self.DirichletDatum - else: - u0Re, u0Im = fenZERO, fenZERO - if j < self.nbs: - fRe, fIm = self.forcingTerm - cRe, cIm = self.getExtraFactorB(self.mu0, derH[-1]) - cfRe, cfIm = cRe * fRe - cIm * fIm, cRe * fIm + cIm * fRe - else: - cfRe, cfIm = fenZERO, fenZERO - parsRe = self.iterReduceQuadratureDegree(zip([cfRe], - ["forcingTermDer{}Real".format(j)])) - parsIm = self.iterReduceQuadratureDegree(zip([cfIm], - ["forcingTermDer{}Imag".format(j)])) - L0Re = fen.dot(cfRe, self.v) * fen.dx - L0Im = fen.dot(cfIm, self.v) * fen.dx - DBCR = fen.DirichletBC(self.V, u0Re, self.DirichletBoundary) - DBCI = fen.DirichletBC(self.V, u0Im, self.DirichletBoundary) - b = (fenics2Vector(L0Re, parsRe, DBCR, 1) - + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1)) + vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), + 20) + if bDEIMCoeffs is None: + bDEIM = np.empty((np.sum(hashI(nbsTot, self.npar)), + self.spacedim()), dtype = np.complex) + muDEIM = np.linspace(.5, 4., bDEIM.shape[0]) + for jj, muD in enumerate(muDEIM): + fRe, fIm = self.getForcingTerm(muD) + parsRe = self.iterReduceQuadratureDegree(zip([fRe], + ["forcingTerm{}Real".format(jj)])) + parsIm = self.iterReduceQuadratureDegree(zip([fIm], + ["forcingTerm{}Imag".format(jj)])) + LR = fen.dot(fRe, self.v) * fen.dx + LI = fen.dot(fIm, self.v) * fen.dx + DBC0 = fen.DirichletBC(self.V, fenZERO, + self.DirichletBoundary) + bDEIM[jj] = (fenics2Vector(LR, parsRe, DBC0, 1) + + 1.j * fenics2Vector(LI, parsIm, DBC0, 1)) + bDEIMCoeffs = fit(muDEIM, bDEIM, len(muDEIM) - 1) + b = bDEIMCoeffs[derH[-1]] if homogeneized: - Ader = self.A(self.mu0, derH) + Ader = self.A(0, derH) b -= Ader.dot(self.liftedDirichletDatum) if homogeneized: self.bsH[j] = b else: self.bs[j] = b - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling forcing term.", - timestamp = self.timestamp) - return self._assembleb(mu, der, derI, homogeneized, self.mu0) + vbMng(self, "DEL", "Done assembling forcing term.", 20) + return self._assembleb(mu, der, derI, homogeneized) diff --git a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py b/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py index 68a0f00..b99b9e4 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py +++ b/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py @@ -1,106 +1,94 @@ # 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 ScOp, List, paramVal from rrompy.solver.fenics import fenZERO from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) from rrompy.solver.fenics import fenics2Sparse __all__ = ['HelmholtzSquareSimplifiedDomainProblemEngine'] class HelmholtzSquareSimplifiedDomainProblemEngine(HelmholtzProblemEngine): """ Solver for square Helmholtz problems with parametric laplacian. - \dxx u - mu_2**2 \dyy u - mu_1**2 * u = f in \Omega_mu = [0,\pi]^2 u = 0 on \partial\Omega """ def __init__(self, kappa:float, theta:float, n:int, mu0 : paramVal = [12. ** .5, 1.], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs = 3 self.npar = 2 self.rescalingExp = [2., 2.] pi = np.pi mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(pi, pi), 3 * n, 3 * n) self.V = fen.FunctionSpace(mesh, "P", 1) c, s = np.cos(theta), np.sin(theta) x, y = fen.SpatialCoordinate(mesh)[:] self.forcingTerm = [fen.cos(kappa * (c * x + s * y)), fen.sin(kappa * (c * x + s * y))] def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 1 and self.As[1] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a1Re = - n2Re * fen.dot(self.u, self.v) * fen.dx a1Im = - n2Im * fen.dot(self.u, self.v) * fen.dx self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 2 and self.As[2] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A2.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a2Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx self.As[2] = fenics2Sparse(a2Re, {}, DirichletBC0, 0) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) diff --git a/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py b/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py index cd3ec26..73cd317 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py +++ b/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py @@ -1,123 +1,115 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from rrompy.utilities.base.types import Np1D, Tuple, List, FenExpr, paramVal from rrompy.hfengines.linear_problem.laplace_disk_gaussian import ( LaplaceDiskGaussian) from rrompy.solver.fenics import fenZERO, fenONE -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) from rrompy.utilities.exception_manager import RROMPyException from rrompy.solver.fenics import fenics2Vector __all__ = ['LaplaceDiskGaussian2'] class LaplaceDiskGaussian2(LaplaceDiskGaussian): """ Solver for disk Laplace problems with parametric forcing term center. - \Delta u = C exp(-.5 * ||\cdot - (mu1, mu2)||^2) in \Omega = B(0, 5) u = 0 on \partial\Omega. """ def __init__(self, n:int, mu0 : paramVal = [0., 0.], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(n = n, mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nbs = 1 self.npar = 2 def getForcingTerm(self, mu : paramVal = []) -> Tuple[FenExpr, FenExpr]: """Compute forcing term.""" mu = self.checkParameter(mu) if mu != self.forcingTermMu: - if self.verbosity >= 25: - verbosityDepth("INIT", ("Assembling base expression for " - "forcing term."), - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling base expression for forcing term.", + 25) x, y = fen.SpatialCoordinate(self.V.mesh())[:] C = np.exp(-.5 * (mu(0, 0) ** 2. + mu(0, 1) ** 2.)) CR, CI = np.real(C), np.imag(C) f0 = (2 * np.pi) ** -.5 * fen.exp(-.5 * (x ** 2. + y ** 2.)) muxR, muxI = np.real(mu(0, 0)), np.imag(mu(0, 0)) muyR, muyI = np.real(mu(0, 1)), np.imag(mu(0, 1)) f1R = fen.exp(muxR * x + muyR * y) * fen.cos(muxI * x + muyI * y) f1I = fen.exp(muxR * x + muyR * y) * fen.sin(muxI * x + muyI * y) self.forcingTerm = [f0 * (CR * f1R - CI * f1I), f0 * (CR * f1I + CI * f1R)] self.forcingTermMu = mu - if self.verbosity >= 25: - verbosityDepth("DEL", "Done assembling base expression.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling base expression.", 25) return self.forcingTerm def computebsFactors(self): pass def getExtraFactorB(self, mu : paramVal = [], derI : int = 0) -> Tuple[FenExpr, FenExpr]: if derI == 0: return [fenONE, fenZERO] raise RROMPyException("Not implemented.") def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) nbsTot = self.nbsH if homogeneized else self.nbs bs = self.bsH if homogeneized else self.bs if homogeneized and self.mu0 != self.mu0BC: self.liftDirichletData(self.mu0) for j in range(derI, nbsTot): if bs[j] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", ("Assembling forcing term " - "b{}.").format(j), - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), + 20) if j < self.nbs: fRe, fIm = self.getForcingTerm(mu) cRe, cIm = self.getExtraFactorB(mu, j) cfRe, cfIm = cRe * fRe - cIm * fIm, cRe * fIm + cIm * fRe else: cfRe, cfIm = fenZERO, fenZERO parsRe = self.iterReduceQuadratureDegree(zip([cfRe], ["forcingTermDer{}Real".format(j)])) parsIm = self.iterReduceQuadratureDegree(zip([cfIm], ["forcingTermDer{}Imag".format(j)])) L0Re = fen.dot(cfRe, self.v) * fen.dx L0Im = fen.dot(cfIm, self.v) * fen.dx DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) b = (fenics2Vector(L0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Vector(L0Im, parsIm, DirichletBC0, 1)) if homogeneized: - Ader = self.A(self.mu0, hashI(j, self.npar)) + Ader = self.A(0, hashI(j, self.npar)) b -= Ader.dot(self.liftedDirichletDatum) if homogeneized: self.bsH[j] = b else: self.bs[j] = b - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling forcing term.", - timestamp = self.timestamp) - return self._assembleb(mu, der, derI, homogeneized, self.mu0) + vbMng(self, "DEL", "Done assembling forcing term.", 20) + return self._assembleb(mu, der, derI, homogeneized) diff --git a/rrompy/hfengines/linear_problem/bidimensional/membrane_fracture_engine.py b/rrompy/hfengines/linear_problem/bidimensional/membrane_fracture_engine.py index 98c1119..dd131a4 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/membrane_fracture_engine.py +++ b/rrompy/hfengines/linear_problem/bidimensional/membrane_fracture_engine.py @@ -1,188 +1,156 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen import mshr, ufl from rrompy.utilities.base.types import ScOp, List, paramVal from rrompy.solver.fenics import fenZERO, fenONE from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) from rrompy.solver.fenics import fenics2Sparse __all__ = ['MembraneFractureEngine'] class MembraneFractureEngine(HelmholtzProblemEngine): def __init__(self, mu0 : paramVal = [20. ** .5, .6], H : float = 1., L : float = .75, delta : float = .05, n : int = 50, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs = 20 self.npar = 2 self.H = H self.rescalingExp = [2., 1.] domain = (mshr.Rectangle(fen.Point(0., - H / 2.), fen.Point(2. * L + delta, H / 2.)) - mshr.Rectangle(fen.Point(L, 0.), fen.Point(L + delta, H / 2.))) mesh = mshr.generate_mesh(domain, n) self.V = fen.FunctionSpace(mesh, "P", 1) self.NeumannBoundary = lambda x, on_b: (on_b and x[1] >= - H / 4. and x[0] >= L and x[0] <= L + delta) self.DirichletBoundary = "REST" x, y = fen.SpatialCoordinate(mesh)[:] self._belowIndicator = ufl.conditional(ufl.le(y, 0.), fenONE, fenZERO) self._aboveIndicator = fenONE - self._belowIndicator self.DirichletDatum = [fen.exp(- 10. * (H / 2. + y) / H - .5 * ((x - .6 * L) / (.1 * L)) ** 2. ) * self._belowIndicator, fenZERO] def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() for j in [1, 3, 4, 6, 7, 10, 11, 12, 15, 16, 17, 18]: if derI <= j and self.As[j] is None: self.As[j] = self.checkAInBounds(-1) if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = (self.H ** 4 / 4. * self._aboveIndicator * fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx) self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 2 and self.As[2] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A2.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a2Re = (- self.H ** 3 / 2. * self._aboveIndicator * fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx) self.As[2] = fenics2Sparse(a2Re, {}, DirichletBC0, 0) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 5 and self.As[5] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A6.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A6.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a5Re = self.H ** 2 * (fen.dot(self.u.dx(0), self.v.dx(0)) + .25 * fen.dot(self.u.dx(1), self.v.dx(1))) * fen.dx self.As[5] = fenics2Sparse(a5Re, {}, DirichletBC0, 0) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 8 and self.As[8] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A8.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A8.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a8Re = - self.H ** 2. * n2Re * fen.dot(self.u, self.v) * fen.dx a8Im = - self.H ** 2. * n2Im * fen.dot(self.u, self.v) * fen.dx self.As[8] = (fenics2Sparse(a8Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a8Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 9 and self.As[9] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A9.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A9.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a9Re = - 2. * self.H * fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx self.As[9] = fenics2Sparse(a9Re, {}, DirichletBC0, 0) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 13 and self.As[13] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A13.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A13.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a13Re = 2. * self.H * n2Re * fen.dot(self.u, self.v) * fen.dx a13Im = 2. * self.H * n2Im * fen.dot(self.u, self.v) * fen.dx self.As[13] = (fenics2Sparse(a13Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a13Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 14 and self.As[14] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A14.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A14.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a14Re = fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx self.As[14] = fenics2Sparse(a14Re, {}, DirichletBC0, 0) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 19 and self.As[19] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A19.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A19.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a19Re = - n2Re * fen.dot(self.u, self.v) * fen.dx a19Im = - n2Im * fen.dot(self.u, self.v) * fen.dx self.As[19] = (fenics2Sparse(a19Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a19Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) diff --git a/rrompy/hfengines/linear_problem/bidimensional/synthetic_bivariate_engine.py b/rrompy/hfengines/linear_problem/bidimensional/synthetic_bivariate_engine.py index 2fb7ca5..c7315a7 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/synthetic_bivariate_engine.py +++ b/rrompy/hfengines/linear_problem/bidimensional/synthetic_bivariate_engine.py @@ -1,123 +1,111 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen import ufl from rrompy.utilities.base.types import ScOp, List, paramVal from rrompy.solver.fenics import fenONE, fenZERO from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) from rrompy.solver.fenics import fenics2Sparse __all__ = ['SyntheticBivariateEngine'] class SyntheticBivariateEngine(HelmholtzProblemEngine): def __init__(self, kappa:float, theta:float, n:int, L : int = 2., mu0 : paramVal = [12. ** .5, 15. ** .5], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs = 3 self.npar = 2 self.rescalingExp = [2., 2.] mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(L, L), n, n) self.V = fen.FunctionSpace(mesh, "P", 1) x, y = fen.SpatialCoordinate(mesh)[:] self._above = ufl.conditional(ufl.ge(y, .5 * L), fenONE, fenZERO) self._below = fenONE - self._above c, s = np.cos(theta), np.sin(theta) self.forcingTerm = [fen.cos(kappa * (c * x + s * y)), fen.sin(kappa * (c * x + s * y))] def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) aRe, aIm = self.diffusivity hRe, hIm = self.RobinDatumH termNames = ["diffusivity", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip( [aRe, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [aIm, hIm], [x + "Imag" for x in termNames])) a0Re = (aRe * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hRe * fen.dot(self.u, self.v) * self.ds(1)) a0Im = (aIm * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hIm * fen.dot(self.u, self.v) * self.ds(1)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 1 and self.As[1] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a1Re = - n2Re * self._above * fen.dot(self.u, self.v) * fen.dx a1Im = - n2Im * self._above * fen.dot(self.u, self.v) * fen.dx self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 2 and self.As[2] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A2.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a2Re = - n2Re * self._below * fen.dot(self.u, self.v) * fen.dx a2Im = - n2Im * self._below * fen.dot(self.u, self.v) * fen.dx self.As[2] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) diff --git a/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py index e65c100..ad74541 100644 --- a/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py +++ b/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py @@ -1,145 +1,137 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from .laplace_base_problem_engine import LaplaceBaseProblemEngine from rrompy.utilities.base.types import List, ScOp, paramVal from rrompy.solver.fenics import fenZERO, fenONE -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) from rrompy.solver.fenics import fenics2Sparse __all__ = ['HelmholtzProblemEngine'] class HelmholtzProblemEngine(LaplaceBaseProblemEngine): """ Solver for generic Helmholtz problems with parametric wavenumber. - \nabla \cdot (a \nabla u) - omega^2 * n**2 * u = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu + h u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real FE space. u: Generic trial functions for variational form evaluation. v: Generic test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. bsH: Numpy array representation of homogeneized bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. energyNormDualMatrix: Scipy sparse matrix representing inner product over V'. dualityMatrix: Scipy sparse matrix representing duality V-V'. energyNormPartialDualMatrix: Scipy sparse matrix representing dual inner product between Riesz representers V-V. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. omega: Value of omega. diffusivity: Value of a. refractionIndex: Value of n. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. RobinDatumH: Value of h. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. A1: Scipy sparse array representation (in CSC format) of A1. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ def __init__(self, mu0 : paramVal = [0.], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs = 2 self.rescalingExp = [2.] self.refractionIndex = fenONE @property def refractionIndex(self): """Value of n.""" return self._refractionIndex @refractionIndex.setter def refractionIndex(self, refractionIndex): self.resetAs() if not isinstance(refractionIndex, (list, tuple,)): refractionIndex = [refractionIndex, fenZERO] self._refractionIndex = refractionIndex def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) if derI <= 0 and self.As[0] is None: self.autoSetDS() - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) aRe, aIm = self.diffusivity hRe, hIm = self.RobinDatumH termNames = ["diffusivity", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip( [aRe, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [aIm, hIm], [x + "Imag" for x in termNames])) a0Re = (aRe * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hRe * fen.dot(self.u, self.v) * self.ds(1)) a0Im = (aIm * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hIm * fen.dot(self.u, self.v) * self.ds(1)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 1 and self.As[1] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a1Re = - n2Re * fen.dot(self.u, self.v) * fen.dx a1Im = - n2Im * fen.dot(self.u, self.v) * fen.dx self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) diff --git a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py index 7106dae..a455d2b 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,214 +1,160 @@ # 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 numpy.polynomial.polynomial import polyfit as fit import fenics as fen from rrompy.utilities.base.types import (Np1D, ScOp, Tuple, List, FenExpr, paramVal) from rrompy.solver.fenics import fenZERO from .helmholtz_problem_engine import HelmholtzProblemEngine -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) from rrompy.solver.fenics import fenics2Sparse, fenics2Vector __all__ = ['HelmholtzSquareBubbleDomainProblemEngine'] class HelmholtzSquareBubbleDomainProblemEngine(HelmholtzProblemEngine): """ Solver for square bubble Helmholtz problems with parametric domain heigth. - \Delta u - kappa^2 * u = f in \Omega_mu = [0,\pi] x [0,\mu\pi] u = 0 on \Gamma_mu = \partial\Omega_mu with exact solution square bubble times plane wave. """ def __init__(self, kappa:float, theta:float, n:int, mu0 : paramVal = [1.], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) - self.nAs, self.nbs = 3, 20 + self.nAs, self.nbs = 3, 15 self.kappa = kappa self.theta = theta self.forcingTermMu = np.nan mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(np.pi, np.pi), 3 * n, 3 * n) self.V = fen.FunctionSpace(mesh, "P", 1) self.rescalingExp = [1.] def getForcingTerm(self, mu : paramVal = []) -> Tuple[FenExpr, FenExpr]: """Compute forcing term.""" - mu = self.checkParameter(mu) - if mu != self.forcingTermMu: - if self.verbosity >= 25: - verbosityDepth("INIT", ("Assembling base expression for " - "forcing term."), - timestamp = self.timestamp) - pi = np.pi - c, s = np.cos(self.theta), np.sin(self.theta) - x, y = fen.SpatialCoordinate(self.V.mesh())[:] - muR, muI = np.real(mu(0, 0)), np.imag(mu(0, 0)) - mu2R, mu2I = np.real(mu(0, 0) ** 2.), np.imag(mu(0, 0) ** 2.) - C = 16. / pi ** 4. - bR = C * (2 * (x * (pi - x) + y * (pi - y)) - + (self.kappa * s) ** 2. * (mu2R - 1.) - * x * (pi - x) * y * (pi - y)) - bI = C * (2 * self.kappa * (c * (pi - 2 * x) * y * (pi - y) - + s * x * (pi - x) * (pi - 2 * y)) - + (self.kappa * s) ** 2. * mu2I - * x * (pi - x) * y * (pi - y)) - wR = (fen.cos(self.kappa * (c * x + s * muR * y)) - * fen.exp(self.kappa * s * muI * y)) - wI = (fen.sin(self.kappa * (c * x + s * muR * y)) - * fen.exp(self.kappa * s * muI * y)) - self.forcingTerm = [bR * wR + bI * wI, bI * wR - bR * wI] - self.forcingTermMu = mu - if self.verbosity >= 25: - verbosityDepth("DEL", "Done assembling base expression.", - timestamp = self.timestamp) - return self.forcingTerm + vbMng(self, "INIT", ("Assembling base expression for forcing term " + "at {}.").format(mu), 25) + 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)) + fRe, fIm = bR * wR + bI * wI, bI * wR - bR * wI + forcingTerm = [mu2R * fRe - mu2I * fIm + fenZERO, + mu2R * fIm + mu2I * fRe + fenZERO] + vbMng(self, "DEL", "Done assembling base expression.", 25) + return forcingTerm - def getExtraFactorB(self, mu : paramVal = [], - derI : int = 0) -> Tuple[FenExpr, FenExpr]: - """Compute extra expression in RHS.""" - mu = self.checkParameter(mu) - def getPowMinusj(x, power): - powR = x ** power - powI = fenZERO - if power % 2 == 1: - powR, powI = powI, powR - if (power + 3) % 4 < 2: - powR, powI = - powR, - powI - return powR, powI - if self.verbosity >= 25: - verbosityDepth("INIT", ("Assembling auxiliary expression for " - "forcing term derivative."), - timestamp = self.timestamp) - from scipy.special import factorial as fact - y = fen.SpatialCoordinate(self.V.mesh())[1] - powR, powI = [(self.kappa * np.sin(self.theta)) ** derI * k\ - for k in getPowMinusj(y, derI)] - mu2R, mu2I = np.real(mu(0, 0) ** 2.), np.imag(mu(0, 0) ** 2.) - exprR = mu2R * powR - mu2I * powI - exprI = mu2I * powR + mu2R * powI - if derI >= 1: - muR, muI = np.real(2. * mu(0, 0)), np.imag(2. * mu(0, 0)) - powR, powI = [(self.kappa * np.sin(self.theta)) ** (derI - 1) * k\ - * derI for k in getPowMinusj(y, derI - 1)] - exprR += muR * powR - muI * powI - exprI += muI * powR + muR * powI - if derI >= 2: - powR, powI = [(self.kappa * np.sin(self.theta)) ** (derI - 2) * k\ - * derI * (derI - 1) for k in getPowMinusj(y, derI - 2)] - exprR += powR - exprI += powI - fac = fact(derI) - if self.verbosity >= 25: - verbosityDepth("DEL", "Done assembling auxiliary expression.", - timestamp = self.timestamp) - return [exprR / fac, exprI / fac] - def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 1 and self.As[1] is None: self.As[1] = self.checkAInBounds(-1) if derI <= 2 and self.As[2] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A2.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm k2Re, k2Im = np.real(self.omega ** 2), np.imag(self.omega ** 2) k2n2Re = k2Re * n2Re - k2Im * n2Im k2n2Im = k2Re * n2Im + k2Im * n2Re parsRe = self.iterReduceQuadratureDegree(zip([k2n2Re], ["kappaSquaredRefractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([k2n2Im], ["kappaSquaredRefractionIndexSquaredImag"])) a2Re = (fen.dot(self.u.dx(0), self.v.dx(0)) - k2n2Re * fen.dot(self.u, self.v)) * fen.dx a2Im = - k2n2Im * fen.dot(self.u, self.v) * fen.dx self.As[2] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) nbsTot = self.nbsH if homogeneized else self.nbs bs = self.bsH if homogeneized else self.bs if homogeneized and self.mu0 != self.mu0BC: self.liftDirichletData(self.mu0) + bDEIMCoeffs = None for j in range(derI, nbsTot): if bs[j] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", ("Assembling forcing term " - "b{}.").format(j), - timestamp = self.timestamp) - if j < self.nbs: - fRe, fIm = self.getForcingTerm(self.mu0) - cRe, cIm = self.getExtraFactorB(self.mu0, j) - cfRe, cfIm = cRe * fRe - cIm * fIm, cRe * fIm + cIm * fRe - else: - cfRe, cfIm = fenZERO, fenZERO - parsRe = self.iterReduceQuadratureDegree(zip([cfRe], - ["forcingTermDer{}Real".format(j)])) - parsIm = self.iterReduceQuadratureDegree(zip([cfIm], - ["forcingTermDer{}Imag".format(j)])) - L0Re = fen.dot(cfRe, self.v) * fen.dx - L0Im = fen.dot(cfIm, self.v) * fen.dx - DirichletBC0 = fen.DirichletBC(self.V, fenZERO, + vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), + 20) + if bDEIMCoeffs is None: + bDEIM = np.empty((self.nbs, self.spacedim()), + dtype = np.complex) + muDEIM = np.linspace(.5, 4., self.nbs) + for jj, muD in enumerate(muDEIM): + fRe, fIm = self.getForcingTerm(muD) + parsRe = self.iterReduceQuadratureDegree(zip([fRe], + ["forcingTerm{}Real".format(jj)])) + parsIm = self.iterReduceQuadratureDegree(zip([fIm], + ["forcingTerm{}Imag".format(jj)])) + LR = fen.dot(fRe, self.v) * fen.dx + LI = fen.dot(fIm, self.v) * fen.dx + DBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) - b = (fenics2Vector(L0Re, parsRe, DirichletBC0, 1) - + 1.j * fenics2Vector(L0Im, parsIm, DirichletBC0, 1)) + bDEIM[jj] = (fenics2Vector(LR, parsRe, DBC0, 1) + + 1.j * fenics2Vector(LI, parsIm, DBC0, 1)) + bDEIMCoeffs = fit(muDEIM, bDEIM, self.nbs - 1) + b = bDEIMCoeffs[j] if homogeneized: - Ader = self.A(self.mu0, hashI(j, self.npar)) + Ader = self.A(0, hashI(j, self.npar)) b -= Ader.dot(self.liftedDirichletDatum) if homogeneized: self.bsH[j] = b else: self.bs[j] = b - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling forcing term.", - timestamp = self.timestamp) - return self._assembleb(mu, der, derI, homogeneized, self.mu0) + vbMng(self, "DEL", "Done assembling forcing term.", 20) + return self._assembleb(mu, der, derI, homogeneized) diff --git a/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py b/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py index eecea62..8899593 100644 --- a/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py +++ b/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py @@ -1,366 +1,337 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from rrompy.hfengines.base.problem_engine_base import ProblemEngineBase from rrompy.utilities.base.types import Np1D, List, ScOp, paramVal from rrompy.solver.fenics import (fenZERO, fenONE, L2InverseNormMatrix, H1NormMatrix, Hminus1NormMatrix) -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) from rrompy.parameter import checkParameter from rrompy.solver.fenics import fenics2Sparse, fenics2Vector __all__ = ['LaplaceBaseProblemEngine'] class LaplaceBaseProblemEngine(ProblemEngineBase): """ Solver for generic Laplace problems. - \nabla \cdot (a \nabla u) = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu + h u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real FE space. u: Generic trial functions for variational form evaluation. v: Generic test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. bsH: Numpy array representation of homogeneized bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. energyNormDualMatrix: Scipy sparse matrix representing inner product over V'. dualityMatrix: Scipy sparse matrix representing duality V-V'. energyNormPartialDualMatrix: Scipy sparse matrix representing dual inner product between Riesz representers V-V. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. omega: Value of omega. diffusivity: Value of a. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. RobinDatumH: Value of h. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ _energyDualNormCompress = None def __init__(self, mu0 : paramVal = [], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.mu0 = checkParameter(mu0) self.npar = self.mu0.shape[1] self.omega = np.abs(self.mu0(0, 0)) if self.npar > 0 else 0. self.diffusivity = fenONE self.forcingTerm = fenZERO self.DirichletDatum = fenZERO self.NeumannDatum = fenZERO self.RobinDatumG = fenZERO self.RobinDatumH = fenZERO @property def V(self): """Value of V.""" return self._V @V.setter def V(self, V): ProblemEngineBase.V.fset(self, V) self.dsToBeSet = True @property def diffusivity(self): """Value of a.""" return self._diffusivity @diffusivity.setter def diffusivity(self, diffusivity): self.resetAs() if not isinstance(diffusivity, (list, tuple,)): diffusivity = [diffusivity, fenZERO] self._diffusivity = diffusivity @property def forcingTerm(self): """Value of f.""" return self._forcingTerm @forcingTerm.setter def forcingTerm(self, forcingTerm): self.resetbs() if not isinstance(forcingTerm, (list, tuple,)): forcingTerm = [forcingTerm, fenZERO] self._forcingTerm = forcingTerm @property def DirichletDatum(self): """Value of u0.""" return self._DirichletDatum @DirichletDatum.setter def DirichletDatum(self, DirichletDatum): self.resetbs() if not isinstance(DirichletDatum, (list, tuple,)): DirichletDatum = [DirichletDatum, fenZERO] self._DirichletDatum = DirichletDatum @property def NeumannDatum(self): """Value of g1.""" return self._NeumannDatum @NeumannDatum.setter def NeumannDatum(self, NeumannDatum): self.resetbs() if not isinstance(NeumannDatum, (list, tuple,)): NeumannDatum = [NeumannDatum, fenZERO] self._NeumannDatum = NeumannDatum @property def RobinDatumG(self): """Value of g2.""" return self._RobinDatumG @RobinDatumG.setter def RobinDatumG(self, RobinDatumG): self.resetbs() if not isinstance(RobinDatumG, (list, tuple,)): RobinDatumG = [RobinDatumG, fenZERO] self._RobinDatumG = RobinDatumG @property def RobinDatumH(self): """Value of h.""" return self._RobinDatumH @RobinDatumH.setter def RobinDatumH(self, RobinDatumH): self.resetAs() if not isinstance(RobinDatumH, (list, tuple,)): RobinDatumH = [RobinDatumH, fenZERO] self._RobinDatumH = RobinDatumH @property def DirichletBoundary(self): """Function handle to DirichletBoundary.""" return self.BCManager.DirichletBoundary @DirichletBoundary.setter def DirichletBoundary(self, DirichletBoundary): self.resetAs() self.resetbs() self.BCManager.DirichletBoundary = DirichletBoundary @property def NeumannBoundary(self): """Function handle to NeumannBoundary.""" return self.BCManager.NeumannBoundary @NeumannBoundary.setter def NeumannBoundary(self, NeumannBoundary): self.resetAs() self.resetbs() self.dsToBeSet = True self.BCManager.NeumannBoundary = NeumannBoundary @property def RobinBoundary(self): """Function handle to RobinBoundary.""" return self.BCManager.RobinBoundary @RobinBoundary.setter def RobinBoundary(self, RobinBoundary): self.resetAs() self.resetbs() self.dsToBeSet = True self.BCManager.RobinBoundary = RobinBoundary def autoSetDS(self): """Set FEniCS boundary measure based on boundary function handles.""" if self.dsToBeSet: - if self.verbosity >= 20: - verbosityDepth("INIT", "Initializing boundary measures.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Initializing boundary measures.", 20) mesh = self.V.mesh() NB = self.NeumannBoundary RB = self.RobinBoundary boundary_markers = fen.MeshFunction("size_t", mesh, mesh.topology().dim() - 1) NB.mark(boundary_markers, 0) RB.mark(boundary_markers, 1) self.ds = fen.Measure("ds", domain = mesh, subdomain_data = boundary_markers) self.dsToBeSet = False - if self.verbosity >= 20: - verbosityDepth("DEL", "Done initializing boundary measures.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling boundary measures.", 20) 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) + vbMng(self, "INIT", "Assembling energy matrix.", 20) self.energyNormMatrix = H1NormMatrix(self.V, np.abs(self.omega)**2) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy matrix.", 20) def buildEnergyNormDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy dual matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling energy dual matrix.", 20) self.energyNormDualMatrix = Hminus1NormMatrix( self.V, np.abs(self.omega)**2, compressRank = self._energyDualNormCompress) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy dual matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy dual matrix.", 20) def buildDualityPairingForm(self): """Build sparse matrix (in CSR format) representative of duality.""" - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling duality matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling duality matrix.", 20) self.dualityMatrix = L2InverseNormMatrix( self.V, solverType = self._solver, solverArgs = self._solverArgs, compressRank = self._dualityCompress) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling duality matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling duality matrix.", 20) def buildEnergyNormPartialDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product without duality. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy partial dual matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling energy partial dual matrix.", 20) self.energyNormPartialDualMatrix = Hminus1NormMatrix( self.V, np.abs(self.omega)**2, compressRank = self._energyDualNormCompress, duality = False) - if self.verbosity >= 20: - verbosityDepth("DEL", - "Done assembling energy partial dual matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy partial dual matrix.", 20) def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) if derI <= 0 and self.As[0] is None: self.autoSetDS() - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) aRe, aIm = self.diffusivity hRe, hIm = self.RobinDatumH termNames = ["diffusivity", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip([aRe, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip([aIm, hIm], [x + "Imag" for x in termNames])) a0Re = (aRe * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hRe * fen.dot(self.u, self.v) * self.ds(1)) a0Im = (aIm * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hIm * fen.dot(self.u, self.v) * self.ds(1)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) nbsTot = self.nbsH if homogeneized else self.nbs bs = self.bsH if homogeneized else self.bs if homogeneized and self.mu0 != self.mu0BC: self.liftDirichletData(self.mu0) for j in range(derI, nbsTot): if bs[j] is None: self.autoSetDS() - if self.verbosity >= 20: - verbosityDepth("INIT", ("Assembling forcing term " - "b{}.").format(j), - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), + 20) termNames, terms = [], [] if j == 0: u0Re, u0Im = self.DirichletDatum fRe, fIm = self.forcingTerm g1Re, g1Im = self.NeumannDatum g2Re, g2Im = self.RobinDatumG termNames += ["forcingTerm", "NeumannDatum", "RobinDatumG"] terms += [[fRe, fIm], [g1Re, g1Im], [g2Re, g2Im]] else: u0Re, u0Im = fenZERO, fenZERO fRe, fIm = fenZERO, fenZERO g1Re, g1Im = fenZERO, fenZERO g2Re, g2Im = fenZERO, fenZERO if len(termNames) > 0: parsRe = self.iterReduceQuadratureDegree(zip( [term[0] for term in terms], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [term[1] for term in terms], [x + "Imag" for x in termNames])) else: parsRe, parsIm = {}, {} L0Re = (fen.dot(fRe, self.v) * fen.dx + fen.dot(g1Re, self.v) * self.ds(0) + fen.dot(g2Re, self.v) * self.ds(1)) L0Im = (fen.dot(fIm, self.v) * fen.dx + fen.dot(g1Im, self.v) * self.ds(0) + fen.dot(g2Im, self.v) * self.ds(1)) DBCR = fen.DirichletBC(self.V, u0Re, self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, u0Im, self.DirichletBoundary) b = (fenics2Vector(L0Re, parsRe, DBCR, 1) + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1)) if homogeneized: - Ader = self.A(self.mu0, hashI(j, self.npar)) + Ader = self.A(0, hashI(j, self.npar)) b -= Ader.dot(self.liftedDirichletDatum) if homogeneized: self.bsH[j] = b else: self.bs[j] = b - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling forcing term.", - timestamp = self.timestamp) - return self._assembleb(mu, der, derI, homogeneized, self.mu0) + vbMng(self, "DEL", "Done assembling forcing term.", 20) + return self._assembleb(mu, der, derI, homogeneized) diff --git a/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py b/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py index 227c446..5980548 100644 --- a/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py +++ b/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py @@ -1,158 +1,112 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np +from numpy.polynomial.polynomial import polyfit as fit import fenics as fen from rrompy.utilities.base.types import Np1D, Tuple, FenExpr, paramVal from .laplace_base_problem_engine import LaplaceBaseProblemEngine from rrompy.solver.fenics import fenZERO, fenONE -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) from rrompy.solver.fenics import fenics2Vector __all__ = ['LaplaceDiskGaussian'] class LaplaceDiskGaussian(LaplaceBaseProblemEngine): """ Solver for disk Laplace problems with parametric forcing term center. - \Delta u = C exp(-.5 * ||\cdot - (mu, 0)||^2) in \Omega = B(0, 5) u = 0 on \partial\Omega. """ - def __init__(self, n:int, mu0 : paramVal = [0.], degree_threshold : int = np.inf, - verbosity : int = 10, timestamp : bool = True): + def __init__(self, n:int, mu0 : paramVal = [0.], + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) - self.nbs = 20 - self.computebsFactors() + self.nbs = 19 self.forcingTermMu = np.nan import mshr mesh = mshr.generate_mesh(mshr.Circle(fen.Point(0., 0.), 5.), 3 * n) self.V = fen.FunctionSpace(mesh, "P", 1) def getForcingTerm(self, mu : paramVal = []) -> Tuple[FenExpr, FenExpr]: """Compute forcing term.""" mu = self.checkParameter(mu) - if mu != self.forcingTermMu: - if self.verbosity >= 25: - verbosityDepth("INIT", ("Assembling base expression for " - "forcing term."), - timestamp = self.timestamp) - x, y = fen.SpatialCoordinate(self.V.mesh())[:] - C = np.exp(-.5 * mu(0, 0) ** 2.) - CR, CI = np.real(C), np.imag(C) - f0 = (2 * np.pi) ** -.5 * fen.exp(-.5 * (x ** 2. + y ** 2.)) - muR, muI = np.real(mu(0, 0)), np.imag(mu(0, 0)) - f1R = fen.exp(muR * x) * fen.cos(muI * x) - f1I = fen.exp(muR * x) * fen.sin(muI * x) - self.forcingTerm = [f0 * (CR * f1R - CI * f1I), - f0 * (CR * f1I + CI * f1R)] - self.forcingTermMu = mu - if self.verbosity >= 25: - verbosityDepth("DEL", "Done assembling base expression.", - timestamp = self.timestamp) - return self.forcingTerm - - def computebsFactors(self): - self.bsFactors = np.zeros((self.nbs, self.nbs), dtype = float) - self.bsFactors[0, 0] = 1. - self.bsFactors[1, 1] = 1. - for j in range(2, self.nbs): - l = (j + 1) % 2 + 1 - J = np.arange(l, j + 1, 2) - self.bsFactors[j, J] = self.bsFactors[j - 1, J - 1] - if l == 2: - l = 0 - J = np.arange(l, j, 2) - self.bsFactors[j, J] += np.multiply(- 1 - J, - self.bsFactors[j - 1, J + 1]) - self.bsFactors[j, l : j + 2 : 2] /= j - - def getExtraFactorB(self, mu : paramVal = [], - derI : int = 0) -> Tuple[FenExpr, FenExpr]: - """Compute extra expression in RHS.""" - mu = self.checkParameter(mu) - if self.verbosity >= 25: - verbosityDepth("INIT", ("Assembling auxiliary expression for " - "forcing term derivative."), - timestamp = self.timestamp) + vbMng(self, "INIT", ("Assembling base expression for forcing term " + "at {}.").format(mu), 25) + x, y = fen.SpatialCoordinate(self.V.mesh())[:] + C = np.exp(-.5 * mu(0, 0) ** 2.) + CR, CI = np.real(C), np.imag(C) + f0 = (2 * np.pi) ** -.5 * fen.exp(-.5 * (x ** 2. + y ** 2.)) muR, muI = np.real(mu(0, 0)), np.imag(mu(0, 0)) - x = fen.SpatialCoordinate(self.V.mesh())[0] - l = derI % 2 - if l == 0: - powR, powI = fenONE, fenZERO - else: - powR, powI = x - muR, fen.Constant(muI) - exprR, exprI = [self.bsFactors[derI, l] * k for k in [powR, powI]] - for j in range(l + 2, derI + 1, 2): - for _ in range(2): - powR, powI = (powR * (x - muR) - powI * muI, - powR * muI + powI * (x - muR)) - exprR += self.bsFactors[derI, j] * powR - exprI += self.bsFactors[derI, j] * powI - if self.verbosity >= 25: - verbosityDepth("DEL", "Done assembling auxiliary expression.", - timestamp = self.timestamp) - return[exprR, exprI] - + f1R = fen.exp(muR * x) * fen.cos(muI * x) + f1I = fen.exp(muR * x) * fen.sin(muI * x) + forcingTerm = [f0 * (CR * f1R - CI * f1I) + fenZERO, + f0 * (CR * f1I + CI * f1R) + fenZERO] + vbMng(self, "DEL", "Done assembling base expression.", 25) + return forcingTerm + def b(self, mu : paramVal = [], der : int = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) nbsTot = self.nbsH if homogeneized else self.nbs bs = self.bsH if homogeneized else self.bs if homogeneized and self.mu0 != self.mu0BC: self.liftDirichletData(self.mu0) + bDEIMCoeffs = None for j in range(derI, nbsTot): if bs[j] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", ("Assembling forcing term " - "b{}.").format(j), - timestamp = self.timestamp) - if j < self.nbs: - fRe, fIm = self.getForcingTerm(self.mu0) - cRe, cIm = self.getExtraFactorB(self.mu0, j) - cfRe, cfIm = cRe * fRe - cIm * fIm, cRe * fIm + cIm * fRe - else: - cfRe, cfIm = fenZERO, fenZERO - parsRe = self.iterReduceQuadratureDegree(zip([cfRe], - ["forcingTermDer{}Real".format(j)])) - parsIm = self.iterReduceQuadratureDegree(zip([cfIm], - ["forcingTermDer{}Imag".format(j)])) - L0Re = fen.dot(cfRe, self.v) * fen.dx - L0Im = fen.dot(cfIm, self.v) * fen.dx - DirichletBC0 = fen.DirichletBC(self.V, fenZERO, + vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), + 20) + if bDEIMCoeffs is None: + bDEIM = np.empty((self.nbs, self.spacedim()), + dtype = np.complex) + muDEIM = 3. * np.linspace(0., 1., self.nbs // 2 + 1) ** 2. + muDEIM = np.concatenate((-muDEIM[:0:-1], muDEIM)) + for jj, muD in enumerate(muDEIM): + fRe, fIm = self.getForcingTerm(muD) + parsRe = self.iterReduceQuadratureDegree(zip([fRe], + ["forcingTerm{}Real".format(jj)])) + parsIm = self.iterReduceQuadratureDegree(zip([fIm], + ["forcingTerm{}Imag".format(jj)])) + LR = fen.dot(fRe, self.v) * fen.dx + LI = fen.dot(fIm, self.v) * fen.dx + DBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) - b = (fenics2Vector(L0Re, parsRe, DirichletBC0, 1) - + 1.j * fenics2Vector(L0Im, parsIm, DirichletBC0, 1)) + bDEIM[jj] = (fenics2Vector(LR, parsRe, DBC0, 1) + + 1.j * fenics2Vector(LI, parsIm, DBC0, 1)) + bDEIMCoeffs = (fit(muDEIM / 3., bDEIM, self.nbs - 1).T + * np.power(3., - np.arange(self.nbs))).T + b = bDEIMCoeffs[j] if homogeneized: - Ader = self.A(self.mu0, hashI(j, self.npar)) + Ader = self.A(0, hashI(j, self.npar)) b -= Ader.dot(self.liftedDirichletDatum) if homogeneized: self.bsH[j] = b else: self.bs[j] = b - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling forcing term.", - timestamp = self.timestamp) - return self._assembleb(mu, der, derI, homogeneized, self.mu0) + vbMng(self, "DEL", "Done assembling forcing term.", 20) + return self._assembleb(mu, der, derI, homogeneized) diff --git a/rrompy/hfengines/linear_problem/membrane_fracture_engine_nodomain.py b/rrompy/hfengines/linear_problem/membrane_fracture_engine_nodomain.py index a7ad7bb..90bcde2 100644 --- a/rrompy/hfengines/linear_problem/membrane_fracture_engine_nodomain.py +++ b/rrompy/hfengines/linear_problem/membrane_fracture_engine_nodomain.py @@ -1,104 +1,96 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen import mshr, ufl from rrompy.utilities.base.types import ScOp, List, paramVal from rrompy.solver.fenics import fenZERO, fenONE from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) from rrompy.solver.fenics import fenics2Sparse __all__ = ['MembraneFractureEngineNoDomain'] class MembraneFractureEngineNoDomain(HelmholtzProblemEngine): def __init__(self, mu0 : paramVal = [20. ** .5, .6], H : float = 1., L : float = .75, delta : float = .05, n : int = 50, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0[0], degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.npar = 1 self.lFrac = mu0[1] self.H = H self.rescalingExp = [2.] domain = (mshr.Rectangle(fen.Point(0., - H / 2.), fen.Point(2. * L + delta, H / 2.)) - mshr.Rectangle(fen.Point(L, 0.), fen.Point(L + delta, H / 2.))) mesh = mshr.generate_mesh(domain, n) self.V = fen.FunctionSpace(mesh, "P", 1) self.NeumannBoundary = lambda x, on_b: (on_b and x[1] >= - H / 4. and x[0] >= L and x[0] <= L + delta) self.DirichletBoundary = "REST" x, y = fen.SpatialCoordinate(mesh)[:] self._belowIndicator = ufl.conditional(ufl.le(y, 0.), fenONE, fenZERO) self._aboveIndicator = fenONE - self._belowIndicator self.DirichletDatum = [fen.exp(- 10. * (H / 2. + y) / H - .5 * ((x - .6 * L) / (.1 * L)) ** 2. ) * self._belowIndicator, fenZERO] def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) if derI <= 0 and self.As[0] is None: self.autoSetDS() - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = (fen.dot(self.u.dx(0), self.v.dx(0)) + self.H ** 4 / 4. * (self.lFrac ** -2. * self._aboveIndicator + (self.H - self.lFrac) ** -2. * self._belowIndicator) * fen.dot(self.u.dx(1), self.v.dx(1)) ) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 1 and self.As[1] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a1Re = - n2Re * fen.dot(self.u, self.v) * fen.dx a1Im = - n2Im * fen.dot(self.u, self.v) * fen.dx self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) diff --git a/rrompy/hfengines/linear_problem/scattering_problem_engine.py b/rrompy/hfengines/linear_problem/scattering_problem_engine.py index 2bd8229..38e52e2 100644 --- a/rrompy/hfengines/linear_problem/scattering_problem_engine.py +++ b/rrompy/hfengines/linear_problem/scattering_problem_engine.py @@ -1,155 +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 . # from numpy import inf import fenics as fen from rrompy.utilities.base.types import List, ScOp, paramVal from rrompy.solver.fenics import fenZERO -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from .helmholtz_problem_engine import HelmholtzProblemEngine from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) from rrompy.utilities.exception_manager import RROMPyWarning from rrompy.solver.fenics import fenics2Sparse __all__ = ['ScatteringProblemEngine'] class ScatteringProblemEngine(HelmholtzProblemEngine): """ Solver for scattering problems with parametric wavenumber. - \nabla \cdot (a \nabla u) - omega^2 * n**2 * u = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu +- i omega u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real FE space. u: Generic trial functions for variational form evaluation. v: Generic test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. bsH: Numpy array representation of homogeneized bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. energyNormDualMatrix: Scipy sparse matrix representing inner product over V'. dualityMatrix: Scipy sparse matrix representing duality V-V'. energyNormPartialDualMatrix: Scipy sparse matrix representing dual inner product between Riesz representers V-V. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. signR: Sign in ABC. omega: Value of omega. diffusivity: Value of a. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. A1: Scipy sparse array representation (in CSC format) of A1. A2: Scipy sparse array representation (in CSC format) of A2. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ - signR = - 1. - def __init__(self, mu0 : paramVal = [0.], degree_threshold : int = inf, verbosity : int = 10, timestamp : bool = True): self.silenceWarnings = True super().__init__(mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) del self.silenceWarnings self.nAs = 3 self.rescalingExp = [1.] + self.signR = - 1. @property def RobinDatumH(self): """Value of h.""" return self.signR * self.omega @RobinDatumH.setter def RobinDatumH(self, RobinDatumH): if not hasattr(self, "silenceWarnings"): RROMPyWarning(("Scattering problems do not allow changes of h. " "Ignoring assignment.")) return + @property + def signR(self): + """Value of signR.""" + return self._signR + @signR.setter + def signR(self, signR): + self.resetAs() + self._signR = signR + def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) aRe, aIm = self.diffusivity parsRe = self.iterReduceQuadratureDegree(zip([aRe], ["diffusivityReal"])) parsIm = self.iterReduceQuadratureDegree(zip([aIm], ["diffusivityImag"])) a0Re = aRe * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx a0Im = aIm * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 1 and self.As[1] is None: self.autoSetDS() - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a1 = fen.dot(self.u, self.v) * self.ds(1) self.As[1] = (self.signR * 1.j * fenics2Sparse(a1, {}, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 2 and self.As[2] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A2.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a2Re = - n2Re * fen.dot(self.u, self.v) * fen.dx a2Im = - n2Im * fen.dot(self.u, self.v) * fen.dx self.As[2] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) diff --git a/rrompy/reduction_methods/centered/__init__.py b/rrompy/hfengines/linear_problem/tridimensional/__init__.py similarity index 74% copy from rrompy/reduction_methods/centered/__init__.py copy to rrompy/hfengines/linear_problem/tridimensional/__init__.py index 633b633..f0493e4 100644 --- a/rrompy/reduction_methods/centered/__init__.py +++ b/rrompy/hfengines/linear_problem/tridimensional/__init__.py @@ -1,29 +1,30 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -from .generic_centered_approximant import GenericCenteredApproximant -from .rational_pade import RationalPade -from .rb_centered import RBCentered +from .membrane_fracture_engine3 import MembraneFractureEngine3 +from .scattering_1d import Scattering1d +from .matrix_dynamical_passive import MatrixDynamicalPassive __all__ = [ - 'GenericCenteredApproximant', - 'RationalPade', - 'RBCentered' + 'MembraneFractureEngine3', + 'Scattering1d', + 'MatrixDynamicalPassive' ] + diff --git a/rrompy/hfengines/linear_problem/tridimensional/matrix_dynamical_passive.py b/rrompy/hfengines/linear_problem/tridimensional/matrix_dynamical_passive.py new file mode 100644 index 0000000..02e840b --- /dev/null +++ b/rrompy/hfengines/linear_problem/tridimensional/matrix_dynamical_passive.py @@ -0,0 +1,61 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +import scipy.sparse as sp +from rrompy.utilities.base.types import ListAny, paramVal +from rrompy.hfengines.base.matrix_engine_base import MatrixEngineBase +from rrompy.parameter import checkParameter + +__all__ = ['MatrixDynamicalPassive'] + +class MatrixDynamicalPassive(MatrixEngineBase): + def __init__(self, mu0 : paramVal = [0., 10.], n : int = 1000, + omega0 : ListAny = [1.], domega0 : ListAny = [1.], + b : float = 10., verbosity : int = 10, + timestamp : bool = True): + super().__init__(verbosity = verbosity, timestamp = timestamp) + + self.npar = 1 + len(omega0) + self.mu0 = checkParameter(mu0, self.npar) + self.nAs, self.nbs = 1 + self.npar, 1 + + Asize = 2 + 2 * self.npar + n + + dataA0 = np.concatenate((*tuple([tuple([np.imag(o), - np.real(o), + np.real(o), np.imag(o)]) \ + for o in omega0]), + [1., -200., 200., 1., 1., -400., 400., 1.], + 1. + np.arange(n))) + rowA0 = np.concatenate((np.repeat(np.arange(2 * len(omega0) + 4), 2), + 2 + 2 * self.npar + np.arange(n))) + cB0 = np.repeat(np.arange(0, 2 * len(omega0) + 4, 2), 4) + cB1 = np.tile([0, 1], [1, 2 * len(omega0) + 4]).reshape(-1) + colA0 = np.concatenate((cB0 + cB1, 2 + 2 * self.npar + np.arange(n))) + As = [sp.csr_matrix((dataA0, (rowA0, colA0)), dtype = np.complex, + shape = (Asize, Asize))] + As = As + [1.j * sp.eye(Asize, dtype = np.complex)] + for j, do0 in enumerate(domega0): + As = As + [sp.csr_matrix(([- do0, do0], + ([2*j, 2*j+1], [2*j+1, 2*j])), + dtype = np.complex, + shape = (Asize, Asize))] + self.As = As + self.bs = [np.concatenate((b * np.ones(2 + 2 * self.npar), + np.ones(n)))] + diff --git a/rrompy/hfengines/linear_problem/tridimensional/membrane_fracture_engine3.py b/rrompy/hfengines/linear_problem/tridimensional/membrane_fracture_engine3.py new file mode 100644 index 0000000..dea60df --- /dev/null +++ b/rrompy/hfengines/linear_problem/tridimensional/membrane_fracture_engine3.py @@ -0,0 +1,331 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +import fenics as fen +import ufl +from rrompy.utilities.base.types import ScOp, List, paramVal +from rrompy.solver.fenics import fenZERO, fenONE +from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( + HelmholtzProblemEngine) +from rrompy.utilities.base import verbosityManager as vbMng +from rrompy.utilities.poly_fitting.polynomial import ( + hashDerivativeToIdx as hashD) +from rrompy.solver.fenics import fenics2Sparse + +__all__ = ['MembraneFractureEngine3'] + +def gen_mesh_fracture(W, delta, H, n): + n = 2 * (n // 2) + 1 + xL = np.linspace(-.5 * W, - .5 * delta, n // 2 + 1) + xR = - xL[::-1] + nEff = int(np.ceil(delta / (xL[1] - xL[0]))) + xC = np.linspace(-.5 * delta, .5 * delta, nEff + 1)[1:-1] + yA = np.linspace(-.5 * H, .5 * H, n) + yL = np.linspace(-.5 * H, 0., n // 2 + 1) + editor = fen.MeshEditor() + mesh = fen.Mesh() + editor.open(mesh, "triangle", 2, 2) + editor.init_vertices((2 * n + nEff - 1) * (n // 2 + 1)) + editor.init_cells(2 * (2 * n - 2 + nEff) * (n // 2)) + for j, y in enumerate(yA): + for i, x in enumerate(xL): + editor.add_vertex(i + j * (n // 2 + 1), np.array([x, y])) + for j in range(n - 1): + for i in range(n // 2): + editor.add_cell(2 * (i + j * (n // 2)), + np.array([i + j * (n // 2 + 1), i + 1 + j * (n // 2 + 1), + i + (j + 1) * (n // 2 + 1)], dtype=np.uintp)) + editor.add_cell(2 * (i + j * (n // 2)) + 1, + np.array([i + 1 + j * (n // 2 + 1), i + 1 + (j + 1) * (n // 2 + 1), + i + (j + 1) * (n // 2 + 1)], dtype=np.uintp)) + vBase1, cBase1 = n * (n // 2 + 1), 2 * (n - 1) * (n // 2) + for j, y in enumerate(yA): + for i, x in enumerate(xR): + editor.add_vertex(vBase1 + i + j * (n // 2 + 1), np.array([x, y])) + for j in range(n - 1): + for i in range(n // 2): + editor.add_cell(cBase1 + 2 * (i + j * (n // 2)), + np.array([vBase1 + i + j * (n // 2 + 1), vBase1 + i + 1 + j * (n // 2 + 1), + vBase1 + i + (j + 1) * (n // 2 + 1)], dtype=np.uintp)) + editor.add_cell(cBase1 + 2 * (i + j * (n // 2)) + 1, + np.array([vBase1 + i + 1 + j * (n // 2 + 1), + vBase1 + i + 1 + (j + 1) * (n // 2 + 1), + vBase1 + i + (j + 1) * (n // 2 + 1)], dtype=np.uintp)) + vBase2, cBase2 = 2 * n * (n // 2 + 1), 4 * (n - 1) * (n // 2) + for j, y in enumerate(yL): + for i, x in enumerate(xC): + editor.add_vertex(vBase2 + i + j * (nEff - 1), np.array([x, y])) + for j in range(n // 2): + for i in range(nEff - 2): + editor.add_cell(cBase2 + 2 * (i + j * (nEff - 2)), + np.array([vBase2 + i + j * (nEff - 1), vBase2 + i + 1 + j * (nEff - 1), + vBase2 + i + (j + 1) * (nEff - 1)], dtype=np.uintp)) + editor.add_cell(cBase2 + 2 * (i + j * (nEff - 2)) + 1, + np.array([vBase2 + i + 1 + j * (nEff - 1), + vBase2 + i + 1 + (j + 1) * (nEff - 1), + vBase2 + i + (j + 1) * (nEff - 1)], dtype=np.uintp)) + if nEff == 1: + for j in range(n // 2): + editor.add_cell(cBase2 + 2 * j, + np.array([(j + 1) * (n // 2 + 1) - 1, vBase1 + j * (n // 2 + 1), + (j + 2) * (n // 2 + 1) - 1], dtype=np.uintp)) + editor.add_cell(cBase2 + 2 * j + 1, + np.array([vBase1 + j * (n // 2 + 1), vBase1 + (j + 1) * (n // 2 + 1), + (j + 2) * (n // 2 + 1) - 1], dtype=np.uintp)) + else: + cBase3 = 2 * (2 * n + nEff - 4) * (n // 2) + for j in range(n // 2): + editor.add_cell(cBase3 + 2 * j, + np.array([(j + 1) * (n // 2 + 1) - 1, vBase2 + j * (nEff - 1), + (j + 2) * (n // 2 + 1) - 1], dtype=np.uintp)) + editor.add_cell(cBase3 + 2 * j + 1, + np.array([vBase2 + j * (nEff - 1), vBase2 + (j + 1) * (nEff - 1), + (j + 2) * (n // 2 + 1) - 1], dtype=np.uintp)) + cBase4 = 2 * (2 * n + nEff - 3) * (n // 2) + for j in range(n // 2): + editor.add_cell(cBase4 + 2 * j, + np.array([vBase2 + (j + 1) * (nEff - 1) - 1, vBase1 + j * (n // 2 + 1), + vBase2 + (j + 2) * (nEff - 1) - 1], dtype=np.uintp)) + editor.add_cell(cBase4 + 2 * j + 1, + np.array([vBase1 + j * (n // 2 + 1), vBase1 + (j + 1) * (n // 2 + 1), + vBase2 + (j + 2) * (nEff - 1) - 1], dtype=np.uintp)) + editor.close() + return mesh + +class MembraneFractureEngine3(HelmholtzProblemEngine): + + def __init__(self, mu0 : paramVal = [20. ** .5, .6, .08], H : float = 1., + L : float = .75, delta : float = .05, n : int = 50, + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): + super().__init__(mu0 = mu0, degree_threshold = degree_threshold, + verbosity = verbosity, timestamp = timestamp) + self.nAs = 206 + self.npar = 3 + self._H = H + self._delta = delta + self._L = L + self._W = 2 * L + delta + self.rescalingExp = [2., 1., 1.] + mesh = gen_mesh_fracture(self._W, delta, H, n) + self.V = fen.FunctionSpace(mesh, "P", 1) + self.NeumannBoundary = lambda x, on_b: (on_b and x[1] >= - H / 4. + and x[0] >= - .5 * delta and x[0] <= .5 * delta) + self.DirichletBoundary = "REST" + + x, y = fen.SpatialCoordinate(mesh)[:] + self._aboveIndicator = ufl.conditional(ufl.gt(y, 0.), fenONE, fenZERO) + self._belowCenterIndicator = ufl.conditional( + ufl.And(ufl.ge(x, - .5 * delta), + ufl.le(x, .5 * delta)), + fenONE, fenZERO) + self._belowSidesIndicator = (fenONE - self._aboveIndicator + - self._belowCenterIndicator) + self.DirichletDatum = [fen.exp(- 10. * (H / 2. + y) / H + - .5 * ((x + .4 * L + .5 * delta) / (.1 * L)) ** 2. + ) * self._belowSidesIndicator, fenZERO] + + def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: + """Assemble (derivative of) operator of linear system.""" + mu = self.checkParameter(mu) + if not hasattr(der, "__len__"): der = [der] * self.npar + derI = hashD(der) + self.autoSetDS() + + spKx, spKy = [None] * 2, [None] * 2 + spM = None + def getstiffnessblockj(direc:int, j:int): + DirichletBC0 = fen.DirichletBC(self.V, fenZERO, + self.DirichletBoundary) + if direc == 0: + if j == 0: + fact = 16. * self._L ** 2. + else: + fact = 4. * self._delta ** 2. + else: + fact = self._H ** 2. + if direc == 0: + if j == 0: + fact *= (self._aboveIndicator + self._belowSidesIndicator) + else: + fact *= self._belowCenterIndicator + else: + if j == 0: + fact *= self._aboveIndicator + else: + fact *= (self._belowSidesIndicator + + self._belowCenterIndicator) + ajRe = fact * self.u.dx(direc) * self.v.dx(direc) * fen.dx + return fenics2Sparse(ajRe, {}, DirichletBC0, 0) + def getmass(): + DirichletBC0 = fen.DirichletBC(self.V, fenZERO, + self.DirichletBoundary) + ajRe = - 4. * self.u * self.v * fen.dx + return fenics2Sparse(ajRe, {}, DirichletBC0, 0) + + if derI <= 0 and self.As[0] is None: + vbMng(self, "INIT", "Assembling operator term A0.", 20) + DirichletBC0 = fen.DirichletBC(self.V, fenZERO, + self.DirichletBoundary) + a0Re = fenZERO * fen.dot(self.u, self.v) * fen.dx + self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 7 and self.As[7] is None: + vbMng(self, "INIT", "Assembling operator term A7.", 20) + if spKx[1] is None: spKx[1] = getstiffnessblockj(0, 1) + self.As[7] = self._W ** 2. * self._H ** 2. * spKx[1] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 9 and self.As[9] is None: + vbMng(self, "INIT", "Assembling operator term A9.", 20) + if spKy[0] is None: spKy[0] = getstiffnessblockj(1, 0) + self.As[9] = self._W ** 2. * self._H ** 2. * spKy[0] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 16 and self.As[16] is None: + vbMng(self, "INIT", "Assembling operator term A16.", 20) + if spKx[1] is None: spKx[1] = getstiffnessblockj(0, 1) + self.As[16] = - 2. * self._W ** 2. * self._H * spKx[1] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 17 and self.As[17] is None: + vbMng(self, "INIT", "Assembling operator term A17.", 20) + if spKx[1] is None: spKx[1] = getstiffnessblockj(0, 1) + self.As[17] = - 2. * self._W * self._H ** 2. * spKx[1] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 18 and self.As[18] is None: + vbMng(self, "INIT", "Assembling operator term A18.", 20) + if spKy[0] is None: spKy[0] = getstiffnessblockj(1, 0) + self.As[18] = - 2. * self._W ** 2. * self._H * spKy[0] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 19 and self.As[19] is None: + vbMng(self, "INIT", "Assembling operator term A19.", 20) + if spKy[0] is None: spKy[0] = getstiffnessblockj(1, 0) + self.As[19] = - 2. * self._W * self._H ** 2. * spKy[0] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 30 and self.As[30] is None: + vbMng(self, "INIT", "Assembling operator term A30.", 20) + if spKx[1] is None: spKx[1] = getstiffnessblockj(0, 1) + self.As[30] = self._W ** 2. * spKx[1] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 31 and self.As[31] is None: + vbMng(self, "INIT", "Assembling operator term A31.", 20) + if spKx[1] is None: spKx[1] = getstiffnessblockj(0, 1) + self.As[31] = 4. * self._W * self._H * spKx[1] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 32 and self.As[32] is None: + vbMng(self, "INIT", "Assembling operator term A32.", 20) + if spKx[0] is None: spKx[0] = getstiffnessblockj(0, 0) + if spKx[1] is None: spKx[1] = getstiffnessblockj(0, 1) + if spKy[0] is None: spKy[0] = getstiffnessblockj(1, 0) + if spKy[1] is None: spKy[1] = getstiffnessblockj(1, 1) + self.As[32] = (self._H ** 2. * (spKx[0] + spKx[1]) + + self._W ** 2. * (spKy[0] + spKy[1])) + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 33 and self.As[33] is None: + vbMng(self, "INIT", "Assembling operator term A33.", 20) + if spKy[0] is None: spKy[0] = getstiffnessblockj(1, 0) + self.As[33] = 4. * self._W * self._H * spKy[0] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 34 and self.As[34] is None: + vbMng(self, "INIT", "Assembling operator term A34.", 20) + if spKy[0] is None: spKy[0] = getstiffnessblockj(1, 0) + self.As[34] = self._H ** 2. * spKy[0] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 47 and self.As[47] is None: + vbMng(self, "INIT", "Assembling operator term A47.", 20) + if spM is None: spM = getmass() + self.As[47] = self._W ** 2. * self._H ** 2. * spM + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 51 and self.As[51] is None: + vbMng(self, "INIT", "Assembling operator term A51.", 20) + if spKx[1] is None: spKx[1] = getstiffnessblockj(0, 1) + self.As[51] = - 2. * self._W * spKx[1] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 52 and self.As[52] is None: + vbMng(self, "INIT", "Assembling operator term A52.", 20) + if spKx[0] is None: spKx[0] = getstiffnessblockj(0, 0) + if spKx[1] is None: spKx[1] = getstiffnessblockj(0, 1) + self.As[52] = - 2. * self._H * (spKx[0] + spKx[1]) + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 53 and self.As[53] is None: + vbMng(self, "INIT", "Assembling operator term A53.", 20) + if spKy[0] is None: spKy[0] = getstiffnessblockj(1, 0) + if spKy[1] is None: spKy[1] = getstiffnessblockj(1, 1) + self.As[53] = - 2. * self._W * (spKy[0] + spKy[1]) + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 54 and self.As[54] is None: + vbMng(self, "INIT", "Assembling operator term A54.", 20) + if spKy[0] is None: spKy[0] = getstiffnessblockj(1, 0) + self.As[54] = - 2. * self._H * spKy[0] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 73 and self.As[73] is None: + vbMng(self, "INIT", "Assembling operator term A73.", 20) + if spM is None: spM = getmass() + self.As[73] = - 2. * self._W ** 2. * self._H * spM + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 74 and self.As[74] is None: + vbMng(self, "INIT", "Assembling operator term A74.", 20) + if spM is None: spM = getmass() + self.As[74] = - 2. * self._W * self._H ** 2. * spM + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 79 and self.As[79] is None: + vbMng(self, "INIT", "Assembling operator term A79.", 20) + if spKx[0] is None: spKx[0] = getstiffnessblockj(0, 0) + if spKx[1] is None: spKx[1] = getstiffnessblockj(0, 1) + self.As[79] = spKx[0] + spKx[1] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 81 and self.As[81] is None: + vbMng(self, "INIT", "Assembling operator term A81.", 20) + if spKy[0] is None: spKy[0] = getstiffnessblockj(1, 0) + if spKy[1] is None: spKy[1] = getstiffnessblockj(1, 1) + self.As[81] = spKy[0] + spKy[1] + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 107 and self.As[107] is None: + vbMng(self, "INIT", "Assembling operator term A107.", 20) + if spM is None: spM = getmass() + self.As[107] = self._W ** 2. * spM + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 108 and self.As[108] is None: + vbMng(self, "INIT", "Assembling operator term A108.", 20) + if spM is None: spM = getmass() + self.As[108] = 4. * self._W * self._H * spM + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 109 and self.As[109] is None: + vbMng(self, "INIT", "Assembling operator term A109.", 20) + if spM is None: spM = getmass() + self.As[109] = self._H ** 2. * spM + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 151 and self.As[151] is None: + vbMng(self, "INIT", "Assembling operator term A151.", 20) + if spM is None: spM = getmass() + self.As[151] = - 2. * self._W * spM + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 152 and self.As[152] is None: + vbMng(self, "INIT", "Assembling operator term A152.", 20) + if spM is None: spM = getmass() + self.As[152] = - 2. * self._H * spM + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 205 and self.As[205] is None: + vbMng(self, "INIT", "Assembling operator term A205.", 20) + if spM is None: spM = getmass() + self.As[205] = spM + vbMng(self, "DEL", "Done assembling operator term.", 20) + for j in range(derI, self.nAs): + if self.As[j] is None: + self.As[j] = self.checkAInBounds(-1) + return self._assembleA(mu, der, derI) + diff --git a/rrompy/hfengines/linear_problem/tridimensional/scattering_1d.py b/rrompy/hfengines/linear_problem/tridimensional/scattering_1d.py new file mode 100644 index 0000000..bcda6f2 --- /dev/null +++ b/rrompy/hfengines/linear_problem/tridimensional/scattering_1d.py @@ -0,0 +1,78 @@ +# 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 ScOp, List, paramVal +from rrompy.solver.fenics import fenZERO, fenONE +from rrompy.hfengines.linear_problem.scattering_problem_engine import ( + ScatteringProblemEngine) +from rrompy.utilities.base import verbosityManager as vbMng +from rrompy.utilities.poly_fitting.polynomial import ( + hashDerivativeToIdx as hashD) +from rrompy.solver.fenics import fenics2Sparse + +__all__ = ['Scattering1d'] + +class Scattering1d(ScatteringProblemEngine): + + def __init__(self, mu0 : paramVal = [5.5, np.pi, 0.], n : int = 50, + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): + super().__init__(mu0 = mu0, degree_threshold = degree_threshold, + verbosity = verbosity, timestamp = timestamp) + self.nAs = 24 + self.npar = 3 + self.rescalingExp = [1., 1., 1.] + self._L = np.real(mu0[1]) + self.V = fen.FunctionSpace(fen.IntervalMesh(n, 0., self._L), "P", 1) + self.RobinBoundary = lambda x, on_b: (on_b and x[0] >= .5 * np.pi) + self.DirichletBoundary = "REST" + self.DirichletDatum = fenONE + + def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: + """Assemble (derivative of) operator of linear system.""" + mu = self.checkParameter(mu) + if not hasattr(der, "__len__"): der = [der] * self.npar + derI = hashD(der) + if derI <= 0 and self.As[0] is None: + vbMng(self, "INIT", "Assembling operator term A0.", 20) + DirichletBC0 = fen.DirichletBC(self.V, fenZERO, + self.DirichletBoundary) + a0 = fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + self.As[0] = fenics2Sparse(a0, {}, DirichletBC0, 1) + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 14 and self.As[14] is None: + self.autoSetDS() + vbMng(self, "INIT", "Assembling operator term A14.", 20) + DirichletBC0 = fen.DirichletBC(self.V, fenZERO, + self.DirichletBoundary) + a14 = - self._L ** -1. * fen.dot(self.u, self.v) * self.ds(1) + self.As[14] = 1.j * fenics2Sparse(a14, {}, DirichletBC0, 0) + vbMng(self, "DEL", "Done assembling operator term.", 20) + if derI <= 23 and self.As[23] is None: + vbMng(self, "INIT", "Assembling operator term A23.", 20) + DirichletBC0 = fen.DirichletBC(self.V, fenZERO, + self.DirichletBoundary) + a23 = - self._L ** -2. * fen.dot(self.u, self.v) * fen.dx + self.As[23] = fenics2Sparse(a23, {}, DirichletBC0, 0) + vbMng(self, "DEL", "Done assembling operator term.", 20) + for j in range(derI, self.nAs): + if self.As[j] is None: + self.As[j] = self.checkAInBounds(-1) + return self._assembleA(mu, der, derI) diff --git a/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py b/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py index 80c1a2b..d78b291 100644 --- a/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py +++ b/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py @@ -1,157 +1,135 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from rrompy.hfengines.vector_linear_problem.\ linear_elasticity_beam_poisson_ratio import LinearElasticityBeamPoissonRatio from rrompy.solver.fenics import fenZEROS from rrompy.utilities.base.types import Np1D, List, ScOp, paramVal -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.exception_manager import RROMPyAssert from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) from rrompy.solver.fenics import fenics2Sparse, fenics2Vector __all__ = ['LinearElasticityBeamElasticityConstants'] class LinearElasticityBeamElasticityConstants( LinearElasticityBeamPoissonRatio): """ Solver for linear elasticity problem of a beam subject to its own weight, with parametric Joung modulus and Poisson's ratio. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) = rho_ * g in \Omega u = 0 on \Gamma_D \partial_nu = 0 on \Gamma_N """ def __init__(self, n:int, rho_:float, g:float, E0:float, nu0:float, length:float, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = [nu0, E0], degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs, self.nbs = 5, 4 mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(length, 1), n, max(int(n / length), 1)) self.V = fen.VectorFunctionSpace(mesh, "P", 1) self.forcingTerm = [fen.Constant((0., - rho_ * g)), fenZEROS(2)] self.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[0], 0.) self.NeumannBoundary = "REST" def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() for j in [1, 3]: if derI <= j and self.As[j] is None: self.As[j] = self.checkAInBounds(-1) if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) a0Re = fen.inner(fenZEROS(2), self.v) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 4 and self.As[2] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A2.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u)) a2Re = 2. * fen.inner(epsilon(self.u), epsilon(self.v)) * fen.dx self.As[2] = fenics2Sparse(a2Re, {}, DirichletBC0, 0) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 4 and self.As[4] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A4.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A4.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) a4Re = fen.div(self.u) * fen.div(self.v) * fen.dx self.As[4] = (fenics2Sparse(a4Re, {}, DirichletBC0, 0) - 2. * self.As[2]) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" RROMPyAssert(homogeneized, False, "Homogeneized") mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) if derI <= 0 and self.bs[0] is None: self.autoSetDS() - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling forcing term b0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling forcing term b0.", 20) 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 DBCR = fen.DirichletBC(self.V, self.DirichletDatum[0], self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, self.DirichletDatum[1], self.DirichletBoundary) self.bs[0] = (fenics2Vector(L0Re, parsRe, DBCR, 1) + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1)) if derI <= 3 and self.bs[1] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling forcing term b1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling forcing term b1.", 20) fRe, fIm = self.forcingTerm parsRe = self.iterReduceQuadratureDegree(zip([fRe], ["forcingTermReal"])) parsIm = self.iterReduceQuadratureDegree(zip([fIm], ["forcingTermImag"])) L1Re = - fen.inner(fRe, self.v) * fen.dx L1Im = - fen.inner(fIm, self.v) * fen.dx DBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) self.bs[1] = (fenics2Vector(L1Re, parsRe, DBC0, 1) + 1.j * fenics2Vector(L1Im, parsIm, DBC0, 1)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling forcing term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling forcing term.", 20) if derI <= 2 and self.bs[2] is None: self.bs[2] = self.checkbInBounds(-1) if derI <= 3 and self.bs[3] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling forcing term b3.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling forcing term b3.", 20) self.bs[3] = 2. * self.bs[1] - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling forcing term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling forcing term.", 20) return self._assembleb(mu, der, derI, homogeneized) diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py index 096c4ce..03a50a8 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,145 +1,127 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from .linear_elasticity_problem_engine import LinearElasticityProblemEngine from rrompy.solver.fenics import fenZEROS from rrompy.utilities.base.types import Np1D, List, ScOp, paramVal -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.exception_manager import RROMPyAssert from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) from rrompy.solver.fenics import fenics2Sparse, fenics2Vector __all__ = ['LinearElasticityBeamPoissonRatio'] class LinearElasticityBeamPoissonRatio(LinearElasticityProblemEngine): """ Solver for linear elasticity problem of a beam subject to its own weight, with parametric Poisson's ratio. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) = rho_ * g in \Omega u = 0 on \Gamma_D \partial_nu = 0 on \Gamma_N """ def __init__(self, n:int, rho_:float, g:float, E:float, nu0:float, length:float, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = [nu0], degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs, self.nbs = 2, 3 self.E_ = E mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(length, 1), n, max(int(n / length), 1)) self.V = fen.VectorFunctionSpace(mesh, "P", 1) self.forcingTerm = [fen.Constant((0., - rho_ * g / E)), fenZEROS(2)] self.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[0], 0.) self.NeumannBoundary = "REST" def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u)) a0Re = 2. * self.E_ * fen.inner(epsilon(self.u), epsilon(self.v)) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 1 and self.As[1] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u)) a1Re = self.E_ * (fen.div(self.u) * fen.div(self.v) - 4. * fen.inner(epsilon(self.u), epsilon(self.v))) * fen.dx self.As[1] = fenics2Sparse(a1Re, {}, DirichletBC0, 0) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" RROMPyAssert(homogeneized, False, "Homogeneized") mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) if derI <= 0 and self.bs[0] is None: self.autoSetDS() - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling forcing term b0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling forcing term b0.", 20) 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 DBCR = fen.DirichletBC(self.V, self.DirichletDatum[0], self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, self.DirichletDatum[1], self.DirichletBoundary) self.bs[0] = (fenics2Vector(L0Re, parsRe, DBCR, 1) + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1)) if derI <= 2 and self.bs[1] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling forcing term b1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling forcing term b1.", 20) fRe, fIm = self.forcingTerm parsRe = self.iterReduceQuadratureDegree(zip([fRe], ["forcingTermReal"])) parsIm = self.iterReduceQuadratureDegree(zip([fIm], ["forcingTermImag"])) L1Re = - fen.inner(fRe, self.v) * fen.dx L1Im = - fen.inner(fIm, self.v) * fen.dx DBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) self.bs[1] = (fenics2Vector(L1Re, parsRe, DBC0, 1) + 1.j * fenics2Vector(L1Im, parsIm, DBC0, 1)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling forcing term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling forcing term.", 20) if derI <= 2 and self.bs[2] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling forcing term b2.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling forcing term b2.", 20) self.bs[2] = 2. * self.bs[1] - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling forcing term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling forcing term.", 20) return self._assembleb(mu, der, derI, homogeneized) diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine.py index 8f68161..d8b192a 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,204 +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 import fenics as fen from .linear_elasticity_problem_engine import LinearElasticityProblemEngine from rrompy.utilities.base.types import List, ScOp, paramVal from rrompy.solver.fenics import (fenZERO, fenZEROS, fenONE, L2NormMatrix, elasticNormMatrix, elasticDualNormMatrix) -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) from rrompy.solver.fenics import fenics2Sparse __all__ = ['LinearElasticityHelmholtzProblemEngine'] class LinearElasticityHelmholtzProblemEngine(LinearElasticityProblemEngine): """ Solver for generic linear elasticity Helmholtz problems with parametric wavenumber. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) - rho_ * mu^2 * u = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu + h u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real vector FE space. u: Generic vector trial functions for variational form evaluation. v: Generic vector test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. energyNormDualMatrix: Scipy sparse matrix representing inner product over V'. dualityMatrix: Scipy sparse matrix representing duality V-V'. energyNormPartialDualMatrix: Scipy sparse matrix representing dual inner product between Riesz representers V-V. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. omega: Value of omega. lambda_: Value of lambda_. mu_: Value of mu_. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. RobinDatumH: Value of h. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. A1: Scipy sparse array representation (in CSC format) of A1. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ def __init__(self, mu0 : paramVal = [0.], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = [mu0], degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs = 2 self.omega = np.abs(self.mu0(0, 0)) self.rho_ = fenONE self.rescalingExp = [2.] @property def rho_(self): """Value of rho_.""" return self._rho_ @rho_.setter def rho_(self, rho_): self.resetAs() if not isinstance(rho_, (list, tuple,)): rho_ = [rho_, fenZERO] self._rho_ = rho_ def buildEnergyNormForm(self): # energy + omega norm """ Build sparse matrix (in CSR format) representative of scalar product. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling energy matrix.", 20) self.energyNormMatrix = elasticNormMatrix( self.V, self.lambda_[0], self.mu_[0], np.abs(self.omega)**2 * self.rho_[0]) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy matrix.", 20) def buildEnergyNormDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy dual matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling energy dual matrix.", 20) self.energyNormDualMatrix = elasticDualNormMatrix( self.V, self.lambda_[0], self.mu_[0], np.abs(self.omega)**2 * self.rho_[0], compressRank = self._energyDualNormCompress) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy dual matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy dual matrix.", 20) def buildEnergyNormPartialDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product without duality. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy partial dual matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling energy partial dual matrix.", 20) self.energyNormPartialDualMatrix = elasticDualNormMatrix( self.V, self.lambda_[0], self.mu_[0], np.abs(self.omega)**2 * self.rho_[0], compressRank = self._energyDualNormCompress, duality = False) - if self.verbosity >= 20: - verbosityDepth("DEL", - "Done assembling energy partial dual matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy partial dual matrix.", 20) def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) lambda_Re, lambda_Im = self.lambda_ mu_Re, mu_Im = self.mu_ hRe, hIm = self.RobinDatumH termNames = ["lambda_", "mu_", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip( [lambda_Re, mu_Re, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [lambda_Im, mu_Re, hIm], [x + "Imag" for x in termNames])) epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u)) sigma = lambda u, l_, m_: ( l_ * fen.div(u) * fen.Identity(u.geometric_dimension()) + 2. * m_ * epsilon(u)) a0Re = (fen.inner(sigma(self.u, lambda_Re, mu_Re), epsilon(self.v)) * fen.dx + hRe * fen.inner(self.u, self.v) * self.ds(1)) a0Im = (fen.inner(sigma(self.u, lambda_Im, mu_Im), epsilon(self.v)) * fen.dx + hIm * fen.inner(self.u, self.v) * self.ds(1)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 1 and self.As[1] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) rho_Re, rho_Im = self.rho_ parsRe = self.iterReduceQuadratureDegree(zip([rho_Re], ["rho_Real"])) parsIm = self.iterReduceQuadratureDegree(zip([rho_Im], ["rho_Imag"])) a1Re = - rho_Re * fen.inner(self.u, self.v) * fen.dx a1Im = - rho_Im * fen.inner(self.u, self.v) * fen.dx self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine_damped.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine_damped.py index 2642941..9902192 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,180 +1,168 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from .linear_elasticity_helmholtz_problem_engine import \ LinearElasticityHelmholtzProblemEngine from rrompy.utilities.base.types import List, ScOp, paramVal from rrompy.solver.fenics import fenZERO, fenZEROS -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) from rrompy.solver.fenics import fenics2Sparse __all__ = ['LinearElasticityHelmholtzProblemEngineDamped'] class LinearElasticityHelmholtzProblemEngineDamped( LinearElasticityHelmholtzProblemEngine): """ Solver for generic linear elasticity Helmholtz problems with parametric wavenumber. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) - rho_ * (mu^2 - i * eta * mu) * u = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu + h u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real vector FE space. u: Generic vector trial functions for variational form evaluation. v: Generic vector test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. energyNormDualMatrix: Scipy sparse matrix representing inner product over V'. dualityMatrix: Scipy sparse matrix representing duality V-V'. energyNormPartialDualMatrix: Scipy sparse matrix representing dual inner product between Riesz representers V-V. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. omega: Value of omega. lambda_: Value of lambda_. mu_: Value of mu_. eta: Value of eta. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. RobinDatumH: Value of h. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. A1: Scipy sparse array representation (in CSC format) of A1. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ def __init__(self, mu0 : paramVal = [0.], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = [mu0], degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs = 3 self.eta = fenZERO self.rescalingExp = [1.] @property def eta(self): """Value of eta.""" return self._eta @eta.setter def eta(self, eta): self.resetAs() if not isinstance(eta, (list, tuple,)): eta = [eta, fenZERO] self._eta = eta def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) lambda_Re, lambda_Im = self.lambda_ mu_Re, mu_Im = self.mu_ hRe, hIm = self.RobinDatumH termNames = ["lambda_", "mu_", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip( [lambda_Re, mu_Re, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [lambda_Im, mu_Re, hIm], [x + "Imag" for x in termNames])) epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u)) sigma = lambda u, l_, m_: ( l_ * fen.div(u) * fen.Identity(u.geometric_dimension()) + 2. * m_ * epsilon(u)) a0Re = (fen.inner(sigma(self.u, lambda_Re, mu_Re), epsilon(self.v)) * fen.dx + hRe * fen.inner(self.u, self.v) * self.ds(1)) a0Im = (fen.inner(sigma(self.u, lambda_Im, mu_Im), epsilon(self.v)) * fen.dx + hIm * fen.inner(self.u, self.v) * self.ds(1)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 1 and self.As[1] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A1.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) rho_Re, rho_Im = self.rho_ eta_Re, eta_Im = self.eta termNames = ["rho_", "eta"] parsRe = self.iterReduceQuadratureDegree(zip([rho_Re, eta_Re], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip([rho_Im, eta_Im], [x + "Imag" for x in termNames])) a1Re = - ((eta_Re * rho_Im + eta_Im * rho_Re) * fen.inner(self.u, self.v)) * fen.dx a1Im = ((eta_Re * rho_Re - eta_Im * rho_Im) * fen.inner(self.u, self.v)) * fen.dx self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) if derI <= 2 and self.As[2] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A2.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) rho_Re, rho_Im = self.rho_ parsRe = self.iterReduceQuadratureDegree(zip([rho_Re], ["rho_Real"])) parsIm = self.iterReduceQuadratureDegree(zip([rho_Im], ["rho_Imag"])) a2Re = - rho_Re * fen.inner(self.u, self.v) * fen.dx a2Im = - rho_Im * fen.inner(self.u, self.v) * fen.dx self.As[2] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py index 28c8886..25dcbe3 100644 --- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py +++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py @@ -1,392 +1,363 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from rrompy.hfengines.base.vector_problem_engine_base import \ VectorProblemEngineBase from rrompy.utilities.base.types import Np1D, List, ScOp, paramVal from rrompy.solver.fenics import (fenZERO, fenZEROS, fenONE, L2InverseNormMatrix, elasticNormMatrix, elasticDualNormMatrix) -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) from rrompy.parameter import checkParameter from rrompy.solver.fenics import fenics2Sparse, fenics2Vector __all__ = ['LinearElasticityProblemEngine'] class LinearElasticityProblemEngine(VectorProblemEngineBase): """ Solver for generic linear elasticity problems. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu + h u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real vector FE space. u: Generic vector trial functions for variational form evaluation. v: Generic vector test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. energyNormDualMatrix: Scipy sparse matrix representing inner product over V'. dualityMatrix: Scipy sparse matrix representing duality V-V'. energyNormPartialDualMatrix: Scipy sparse matrix representing dual inner product between Riesz representers V-V. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. lambda_: Value of lambda_. mu_: Value of mu_. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. RobinDatumH: Value of h. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ _energyDualNormCompress = None def __init__(self, mu0 : paramVal = [], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.lambda_ = fenONE self.mu_ = fenONE self.mu0 = checkParameter(mu0) self.npar = self.mu0.shape[1] self.forcingTerm = fenZEROS(self.V.mesh().topology().dim()) self.DirichletDatum = fenZEROS(self.V.mesh().topology().dim()) self.NeumannDatum = fenZEROS(self.V.mesh().topology().dim()) self.RobinDatumG = fenZEROS(self.V.mesh().topology().dim()) self.RobinDatumH = fenZERO @property def V(self): """Value of V.""" return self._V @V.setter def V(self, V): VectorProblemEngineBase.V.fset(self, V) self.forcingTerm = fenZEROS(self.V.mesh().topology().dim()) self.DirichletDatum = fenZEROS(self.V.mesh().topology().dim()) self.NeumannDatum = fenZEROS(self.V.mesh().topology().dim()) self.RobinDatumG = fenZEROS(self.V.mesh().topology().dim()) self.dsToBeSet = True @property def lambda_(self): """Value of lambda_.""" return self._lambda_ @lambda_.setter def lambda_(self, lambda_): self.resetAs() if not isinstance(lambda_, (list, tuple,)): lambda_ = [lambda_, fenZERO] self._lambda_ = lambda_ @property def mu_(self): """Value of mu_.""" return self._mu_ @mu_.setter def mu_(self, mu_): self.resetAs() if not isinstance(mu_, (list, tuple,)): mu_ = [mu_, fenZERO] self._mu_ = mu_ @property def forcingTerm(self): """Value of f.""" return self._forcingTerm @forcingTerm.setter def forcingTerm(self, forcingTerm): self.resetbs() if not isinstance(forcingTerm, (list, tuple,)): forcingTerm = [forcingTerm, fenZEROS(self.V.mesh().topology().dim())] self._forcingTerm = forcingTerm @property def DirichletDatum(self): """Value of u0.""" return self._DirichletDatum @DirichletDatum.setter def DirichletDatum(self, DirichletDatum): self.resetbs() if not isinstance(DirichletDatum, (list, tuple,)): DirichletDatum = [DirichletDatum, fenZEROS(self.V.mesh().topology().dim())] self._DirichletDatum = DirichletDatum @property def NeumannDatum(self): """Value of g1.""" return self._NeumannDatum @NeumannDatum.setter def NeumannDatum(self, NeumannDatum): self.resetbs() if not isinstance(NeumannDatum, (list, tuple,)): NeumannDatum = [NeumannDatum, fenZEROS(self.V.mesh().topology().dim())] self._NeumannDatum = NeumannDatum @property def RobinDatumG(self): """Value of g2.""" return self._RobinDatumG @RobinDatumG.setter def RobinDatumG(self, RobinDatumG): self.resetbs() if not isinstance(RobinDatumG, (list, tuple,)): RobinDatumG = [RobinDatumG, fenZEROS(self.V.mesh().topology().dim())] self._RobinDatumG = RobinDatumG @property def RobinDatumH(self): """Value of h.""" return self._RobinDatumH @RobinDatumH.setter def RobinDatumH(self, RobinDatumH): self.resetAs() if not isinstance(RobinDatumH, (list, tuple,)): RobinDatumH = [RobinDatumH, fenZERO] self._RobinDatumH = RobinDatumH @property def DirichletBoundary(self): """Function handle to DirichletBoundary.""" return self.BCManager.DirichletBoundary @DirichletBoundary.setter def DirichletBoundary(self, DirichletBoundary): self.resetAs() self.resetbs() self.BCManager.DirichletBoundary = DirichletBoundary @property def NeumannBoundary(self): """Function handle to NeumannBoundary.""" return self.BCManager.NeumannBoundary @NeumannBoundary.setter def NeumannBoundary(self, NeumannBoundary): self.resetAs() self.resetbs() self.dsToBeSet = True self.BCManager.NeumannBoundary = NeumannBoundary @property def RobinBoundary(self): """Function handle to RobinBoundary.""" return self.BCManager.RobinBoundary @RobinBoundary.setter def RobinBoundary(self, RobinBoundary): self.resetAs() self.resetbs() self.dsToBeSet = True self.BCManager.RobinBoundary = RobinBoundary def autoSetDS(self): """Set FEniCS boundary measure based on boundary function handles.""" if self.dsToBeSet: - if self.verbosity >= 20: - verbosityDepth("INIT", "Initializing boundary measures.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Initializing boundary measures.", 20) 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) + vbMng(self, "DEL", "Done initializing boundary measures.", 20) 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) + vbMng(self, "INIT", "Assembling energy matrix.", 20) self.energyNormMatrix = elasticNormMatrix(self.V, self.lambda_[0], self.mu_[0]) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy matrix.", 20) def buildEnergyNormDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy dual matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling energy dual matrix.", 20) self.energyNormDualMatrix = elasticDualNormMatrix( self.V, self.lambda_[0], self.mu_[0], compressRank = self._energyDualNormCompress) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy dual matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy dual matrix.", 20) def buildDualityPairingForm(self): """Build sparse matrix (in CSR format) representative of duality.""" - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling duality matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling duality matrix.", 20) self.dualityMatrix = L2InverseNormMatrix( self.V, solverType = self._solver, solverArgs = self._solverArgs, compressRank = self._dualityCompress) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling duality matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling duality matrix.", 20) def buildEnergyNormPartialDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product without duality. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy partial dual matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling energy partial dual matrix.", 20) self.energyNormPartialDualMatrix = elasticDualNormMatrix( self.V, self.lambda_[0], self.mu_[0], compressRank = self._energyDualNormCompress, duality = False) - if self.verbosity >= 20: - verbosityDepth("DEL", - "Done assembling energy partial dual matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling energy partial dual matrix.", 20) def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() if derI <= 0 and self.As[0] is None: - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling operator term A0.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) lambda_Re, lambda_Im = self.lambda_ mu_Re, mu_Im = self.mu_ hRe, hIm = self.RobinDatumH termNames = ["lambda_", "mu_", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip( [lambda_Re, mu_Re, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [lambda_Im, mu_Re, hIm], [x + "Imag" for x in termNames])) epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u)) sigma = lambda u, l_, m_: ( l_ * fen.div(u) * fen.Identity(u.geometric_dimension()) + 2. * m_ * epsilon(u)) a0Re = (fen.inner(sigma(self.u, lambda_Re, mu_Re), epsilon(self.v)) * fen.dx + hRe * fen.inner(self.u, self.v) * self.ds(1)) a0Im = (fen.inner(sigma(self.u, lambda_Im, mu_Im), epsilon(self.v)) * fen.dx + hIm * fen.inner(self.u, self.v) * self.ds(1)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling operator term.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done assembling operator term.", 20) return self._assembleA(mu, der, derI) def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) nbsTot = self.nbsH if homogeneized else self.nbs bs = self.bsH if homogeneized else self.bs if homogeneized and self.mu != self.mu0BC: self.liftDirichletData(self.mu) fenZEROSEff = fenZEROS(self.V.mesh().topology().dim()) for j in range(derI, nbsTot): if bs[j] is None: self.autoSetDS() - if self.verbosity >= 20: - verbosityDepth("INIT", ("Assembling forcing term " - "b{}.").format(j), - timestamp = self.timestamp) + vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), + 20) if j == 0: u0Re, u0Im = self.DirichletDatum fRe, fIm = self.forcingTerm g1Re, g1Im = self.NeumannDatum g2Re, g2Im = self.RobinDatumG else: u0Re, u0Im = fenZEROSEff, fenZEROSEff fRe, fIm = fenZEROSEff, fenZEROSEff g1Re, g1Im = fenZEROSEff, fenZEROSEff g2Re, g2Im = fenZEROSEff, fenZEROSEff termNames = ["forcingTerm", "NeumannDatum", "RobinDatumG"] parsRe = self.iterReduceQuadratureDegree(zip( [fRe, g1Re, g2Re], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [fIm, g1Im, g2Im], [x + "Imag" for x in termNames])) L0Re = (fen.inner(fRe, self.v) * fen.dx + fen.inner(g1Re, self.v) * self.ds(0) + fen.inner(g2Re, self.v) * self.ds(1)) L0Im = (fen.inner(fIm, self.v) * fen.dx + fen.inner(g1Im, self.v) * self.ds(0) + fen.inner(g2Im, self.v) * self.ds(1)) DBCR = fen.DirichletBC(self.V, u0Re, self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, u0Im, self.DirichletBoundary) b = (fenics2Vector(L0Re, parsRe, DBCR, 1) + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1)) if homogeneized: - Ader = self.A(self.mu0, hashI(j, self.npar)) + Ader = self.A(0, hashI(j, self.npar)) b -= Ader.dot(self.liftedDirichletDatum) if homogeneized: self.bsH[j] = b else: self.bs[j] = b - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling forcing term.", - timestamp = self.timestamp) - return self._assembleb(mu, der, derI, homogeneized, self.mu0) + vbMng(self, "DEL", "Done assembling forcing term.", 20) + return self._assembleb(mu, der, derI, homogeneized) diff --git a/rrompy/parameter/parameter_sampling/fft_sampler.py b/rrompy/parameter/parameter_sampling/fft_sampler.py index b59d4e0..4199886 100644 --- a/rrompy/parameter/parameter_sampling/fft_sampler.py +++ b/rrompy/parameter/parameter_sampling/fft_sampler.py @@ -1,50 +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 numpy as np from .generic_sampler import GenericSampler from rrompy.utilities.base.types import List, paramList -from rrompy.utilities.base import lowDiscrepancy, kroneckerer +from rrompy.utilities.numerical import lowDiscrepancy, kroneckerer from rrompy.parameter import checkParameterList __all__ = ['FFTSampler'] class FFTSampler(GenericSampler): """Generator of FFT-type sample points on scaled roots of unity.""" def generatePoints(self, n:List[int], reorder : bool = True) -> paramList: """Array of sample points.""" if not hasattr(n, "__len__"): n = [n] super().generatePoints(n) nleft, nright = 1, np.prod(n) xmat = np.empty((nright, self.npar), dtype = np.complex) for d in range(self.npar): nright //= n[d] - a, b = self.lims(0, d), self.lims(1, d) - if self.scaling is not None: - a, b = self.scaling[d](a), self.scaling[d](b) + a = self.lims(0, d) ** self.scalingExp[d] + b = self.lims(1, d) ** self.scalingExp[d] c, r = (a + b) / 2., np.abs(a - b) / 2. xd = c + r * np.exp(1.j * np.linspace(0, 2 * np.pi, n[d] + 1)[:-1]) - if self.scalingInv is not None: - xd = self.scalingInv[d](xd) + xd **= 1. / self.scalingExp[d] xmat[:, d] = kroneckerer(xd, nleft, nright) nleft *= n[d] if reorder: xmat = xmat[lowDiscrepancy(np.prod(n)), :] - x, _ = checkParameterList(xmat, self.npar) + x = checkParameterList(xmat, self.npar)[0] return x diff --git a/rrompy/parameter/parameter_sampling/generic_sampler.py b/rrompy/parameter/parameter_sampling/generic_sampler.py index 52510f3..30f631a 100644 --- a/rrompy/parameter/parameter_sampling/generic_sampler.py +++ b/rrompy/parameter/parameter_sampling/generic_sampler.py @@ -1,98 +1,79 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from abc import abstractmethod from rrompy.utilities.base.types import List, paramList from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert from rrompy.parameter import checkParameterList __all__ = ['GenericSampler'] class GenericSampler: """ABSTRACT. Generic generator of sample points.""" - def __init__(self, lims:paramList, scaling : List[callable] = None, - scalingInv : List[callable] = None): + def __init__(self, lims:paramList, scalingExp : List[float] = None): self.lims = lims - self.scaling = scaling - self.scalingInv = scalingInv + self.scalingExp = scalingExp def name(self) -> str: return self.__class__.__name__ def __str__(self) -> str: return "{}[{}_{}]".format(self.name(), self.lims[0], self.lims[1]) def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) def __eq__(self, other) -> bool: return self.__dict__ == other.__dict__ @property def npar(self): """Number of parameters.""" return self._lims.shape[1] @property def lims(self): """Value of lims.""" return self._lims @lims.setter def lims(self, lims): - lims, _ = checkParameterList(lims) + lims = checkParameterList(lims)[0] if len(lims) != 2: raise RROMPyException("2 limits must be specified.") self._lims = lims @property - def scaling(self): - """Value of scaling.""" - return self._scaling - @scaling.setter - def scaling(self, scaling): - if scaling is not None: - if not hasattr(scaling, "__len__"): scaling = [scaling] - RROMPyAssert(self.npar, len(scaling), "Number of scaling terms") - if not all([callable(s) for s in scaling]): - raise RROMPyException(("Each value of scaling must be a " - "callable.")) - self._scaling = scaling - - @property - def scalingInv(self): - """Value of scalingInv.""" - return self._scalingInv - @scalingInv.setter - def scalingInv(self, scalingInv): - if scalingInv is not None: - if not hasattr(scalingInv, "__len__"): scalingInv = [scalingInv] - RROMPyAssert(self.npar, len(scalingInv), - "Number of scalingInv terms") - if not all([callable(sInv) for sInv in scalingInv]): - raise RROMPyException(("Each value of scalingInv must be a " - "callable.")) - self._scalingInv = scalingInv + def scalingExp(self): + """Value of scalingExp.""" + return self._scalingExp + @scalingExp.setter + def scalingExp(self, scalingExp): + if scalingExp is None: + scalingExp = [1.] * self.npar + if not hasattr(scalingExp, "__len__"): scalingExp = [scalingExp] + RROMPyAssert(self.npar, len(scalingExp), "Number of scaling terms") + self._scalingExp = scalingExp @abstractmethod def generatePoints(self, n:List[int]) -> paramList: """Array of points.""" if not hasattr(n, "__len__"): n = [n] RROMPyAssert(self.npar, len(n), "Point number") pass diff --git a/rrompy/parameter/parameter_sampling/manual_sampler.py b/rrompy/parameter/parameter_sampling/manual_sampler.py index 9a2c943..42aa93d 100644 --- a/rrompy/parameter/parameter_sampling/manual_sampler.py +++ b/rrompy/parameter/parameter_sampling/manual_sampler.py @@ -1,63 +1,61 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from copy import deepcopy as copy from .generic_sampler import GenericSampler from rrompy.utilities.base.types import List, paramList from rrompy.parameter import checkParameterList __all__ = ['ManualSampler'] class ManualSampler(GenericSampler): """Manual generator of sample points.""" def __init__(self, lims:paramList, points:paramList, - scaling : List[callable] = None, - scalingInv : List[callable] = None): - super().__init__(lims = lims, scaling = scaling, - scalingInv = scalingInv) + scalingExp : List[float] = None): + super().__init__(lims = lims, scalingExp = scalingExp) self.points = points @property def points(self): """Value of points.""" return self._points @points.setter def points(self, points): - points, _ = checkParameterList(points, self.npar) + points = checkParameterList(points, self.npar)[0] self._points = points def __str__(self) -> str: return "{}[{}]".format(self.name(), "_".join(map(str, self.points))) def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) def generatePoints(self, n:int) -> paramList: """Array of sample points.""" if hasattr(n, "__len__"): n = n[0] if n > len(self.points): pts = copy(self.points) for j in range(int(np.ceil(n / len(self.points)))): pts.append(self.points) else: pts = self.points - x, _ = checkParameterList(pts[list(range(n))], self.npar) + x = checkParameterList(pts[list(range(n))], self.npar)[0] return x diff --git a/rrompy/parameter/parameter_sampling/quadrature_sampler.py b/rrompy/parameter/parameter_sampling/quadrature_sampler.py index 4ddfbcf..a08adf8 100644 --- a/rrompy/parameter/parameter_sampling/quadrature_sampler.py +++ b/rrompy/parameter/parameter_sampling/quadrature_sampler.py @@ -1,87 +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 .generic_sampler import GenericSampler from rrompy.utilities.base.types import List, paramList from rrompy.utilities.exception_manager import RROMPyException -from rrompy.utilities.base import lowDiscrepancy, kroneckerer +from rrompy.utilities.numerical import lowDiscrepancy, kroneckerer from rrompy.parameter import checkParameterList __all__ = ['QuadratureSampler'] class QuadratureSampler(GenericSampler): """Generator of quadrature sample points.""" allowedKinds = ["UNIFORM", "CHEBYSHEV", "GAUSSLEGENDRE"] def __init__(self, lims:paramList, kind : str = "UNIFORM", - scaling : List[callable] = None, - scalingInv : List[callable] = None): - super().__init__(lims = lims, scaling = scaling, - scalingInv = scalingInv) + scalingExp : List[float] = None): + super().__init__(lims = lims, scalingExp = scalingExp) self.kind = kind def __str__(self) -> str: return "{}_{}".format(super().__str__(), self.kind) def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) @property def kind(self): """Value of kind.""" return self._kind @kind.setter def kind(self, kind): if kind.upper() not in self.allowedKinds: raise RROMPyException("Generator kind not recognized.") self._kind = kind.upper() def generatePoints(self, n:List[int], reorder : bool = True) -> paramList: """Array of sample points.""" if not hasattr(n, "__len__"): n = [n] super().generatePoints(n) nleft, nright = 1, np.prod(n) xmat = np.empty((nright, self.npar), dtype = self.lims.dtype) for d in range(self.npar): nright //= n[d] - a, b = self.lims(0, d), self.lims(1, d) - if self.scaling is not None: - a, b = self.scaling[d](a), self.scaling[d](b) + a = self.lims(0, d) ** self.scalingExp[d] + b = self.lims(1, d) ** self.scalingExp[d] c, r = (a + b) / 2., (a - b) / 2. dAbs = 2. * np.abs(r) if self.kind == "UNIFORM": xd = np.linspace(a, b, n[d]) elif self.kind == "CHEBYSHEV": nodes, _ = np.polynomial.chebyshev.chebgauss(n[d]) xd = c + r * nodes elif self.kind == "GAUSSLEGENDRE": nodes, _ = np.polynomial.legendre.leggauss(n[d]) xd = c + r * nodes[::-1] - if self.scalingInv is not None: - xd = self.scalingInv[d](xd) + xd **= 1. / self.scalingExp[d] xmat[:, d] = kroneckerer(xd, nleft, nright) nleft *= n[d] nright = np.prod(n) if nright > 1 and reorder: fejerOrdering = [nright - 1] + lowDiscrepancy(nright - 1) xmat = xmat[fejerOrdering, :] - x, _ = checkParameterList(xmat, self.npar) + x = checkParameterList(xmat, self.npar)[0] return x diff --git a/rrompy/parameter/parameter_sampling/quadrature_sampler_total.py b/rrompy/parameter/parameter_sampling/quadrature_sampler_total.py index c08a94c..99d5cf3 100644 --- a/rrompy/parameter/parameter_sampling/quadrature_sampler_total.py +++ b/rrompy/parameter/parameter_sampling/quadrature_sampler_total.py @@ -1,79 +1,60 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from scipy.special import binom, factorial as fact from .quadrature_sampler import QuadratureSampler from rrompy.utilities.base.types import paramList -from rrompy.utilities.base import lowDiscrepancy +from rrompy.utilities.numerical import lowDiscrepancy from rrompy.parameter import checkParameterList __all__ = ['QuadratureSamplerTotal'] class QuadratureSamplerTotal(QuadratureSampler): """ Generator of quadrature sample points for total degree polynomial computations. """ def generatePoints(self, n:int, reorder : bool = True) -> paramList: """Array of sample points.""" if hasattr(n, "__len__"): n = n[0] d = self.npar n1d = int((fact(d) * n) ** (1. / d)) while binom(n1d + d - 1, d) > n: n1d -= 1 x = super().generatePoints([n1d] * d, reorder = False) nTot = n1d ** d indicesBase = np.zeros(nTot, dtype = int) idxBase = [x + 1 for x in lowDiscrepancy(n1d - 1, inverse = True)] linearIdxs = np.array(idxBase + [0]) nleft, nright = 1, nTot for j in range(d): nright //= n1d kronIn = np.repeat(linearIdxs, nright) indicesBase += np.tile(kronIn, nleft) nleft *= n1d keepIdxs = np.zeros(nTot, dtype = bool) keepIdxs[indicesBase < n1d] = True xmat = x.data[keepIdxs, :] if reorder: fejerTot = np.array([nTot - 1] + list(lowDiscrepancy(nTot - 1))) xmat = xmat[np.argsort(np.argsort(fejerTot[keepIdxs])), :] - x, _ = checkParameterList(xmat, d) - -# x = super().generatePoints([n1d] * d, reorder = True)[list(range(n))] -# if not reorder: -# fejerOrderingInv = lowDiscrepancy(n, inverse = True) -# xmat = x.data[fejerOrderingInv, :] -# x, _ = checkParameterList(xmat, d) - -# x = super().generatePoints([n1d] * d, reorder = False)[list(range(n))] -# nTot = n1d ** d -# keepIdxs = np.ones(nTot, dtype = bool) -# eps = nTot * 1e-10 -# gridPts = np.linspace(- eps, nTot - 1 + eps, 2 * (nTot - n) + 1)[1::2] -# keepIdxs[np.round(gridPts).astype(int)] = False -# xmat = x.data[keepIdxs, :] -# if reorder: -# fejerTot = np.array([nTot - 1] + list(lowDiscrepancy(nTot - 1))) -# xmat = xmat[np.argsort(np.argsort(fejerTot[keepIdxs])), :] -# x, _ = checkParameterList(xmat, d) - + x = checkParameterList(xmat, d)[0] return x diff --git a/rrompy/parameter/parameter_sampling/random_sampler.py b/rrompy/parameter/parameter_sampling/random_sampler.py index 4f51013..68625d2 100644 --- a/rrompy/parameter/parameter_sampling/random_sampler.py +++ b/rrompy/parameter/parameter_sampling/random_sampler.py @@ -1,77 +1,72 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from .generic_sampler import GenericSampler -from rrompy.utilities.base.halton import haltonGenerate -from rrompy.utilities.base.sobol import sobolGenerate +from rrompy.utilities.numerical import haltonGenerate, sobolGenerate from rrompy.utilities.base.types import List, paramList from rrompy.utilities.exception_manager import RROMPyException from rrompy.parameter import checkParameterList __all__ = ['RandomSampler'] class RandomSampler(GenericSampler): """Generator of quadrature sample points.""" allowedKinds = ["UNIFORM", "HALTON", "SOBOL"] def __init__(self, lims:paramList, kind : str = "UNIFORM", - scaling : List[callable] = None, - scalingInv : List[callable] = None, seed : int = 42): - super().__init__(lims = lims, scaling = scaling, - scalingInv = scalingInv) + scalingExp : List[float] = None, seed : int = 42): + super().__init__(lims = lims, scalingExp = scalingExp) self.kind = kind self.seed = seed def __str__(self) -> str: return "{}_{}".format(super().__str__(), self.kind) def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) @property def kind(self): """Value of kind.""" return self._kind @kind.setter def kind(self, kind): if kind.upper() not in self.allowedKinds: raise RROMPyException("Generator kind not recognized.") self._kind = kind.upper() def generatePoints(self, n:int) -> paramList: """Array of quadrature points.""" if hasattr(n, "__len__"): n = n[0] if self.kind == "UNIFORM": np.random.seed(self.seed) xmat = np.random.uniform(size = (n, self.npar)) elif self.kind == "HALTON": xmat = haltonGenerate(self.npar, n, self.seed) else: xmat = sobolGenerate(self.npar, n, self.seed) for d in range(self.npar): - a, b = self.lims(0, d), self.lims(1, d) - if self.scaling is not None: - a, b = self.scaling[d](a), self.scaling[d](b) + a = self.lims(0, d) ** self.scalingExp[d] + b = self.lims(1, d) ** self.scalingExp[d] xmat[:, d] = a + (b - a) * xmat[:, d] - if self.scalingInv is not None: - xmat[:, d] = self.scalingInv[d](xmat[:, d]) - x, _ = checkParameterList(xmat, self.npar) + xmat[:, d] **= 1. / self.scalingExp[d] + x = checkParameterList(xmat, self.npar)[0] return x diff --git a/rrompy/reduction_methods/base/__init__.py b/rrompy/reduction_methods/base/__init__.py index 98d784c..f6aa877 100644 --- a/rrompy/reduction_methods/base/__init__.py +++ b/rrompy/reduction_methods/base/__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 import GenericApproximant -from .pade_utils import checkRobustTolerance -from .rb_utils import projectAffineDecomposition +from .rational_interpolant_utils import checkRobustTolerance +from .reduced_basis_utils import projectAffineDecomposition __all__ = [ 'GenericApproximant', 'checkRobustTolerance', 'projectAffineDecomposition' ] diff --git a/rrompy/reduction_methods/base/generic_approximant.py b/rrompy/reduction_methods/base/generic_approximant.py index 25f2a72..12718ee 100644 --- a/rrompy/reduction_methods/base/generic_approximant.py +++ b/rrompy/reduction_methods/base/generic_approximant.py @@ -1,884 +1,886 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from abc import abstractmethod import numpy as np from itertools import product as iterprod from copy import deepcopy as copy from os import remove as osrm -from rrompy.sampling.linear_problem import (SamplingEngineLinear, - SamplingEngineLinearPOD) +from rrompy.sampling.standard import (SamplingEngineStandard, + SamplingEngineStandardPOD) from rrompy.utilities.base.types import (Np1D, DictAny, HFEng, List, Tuple, ListAny, strLst, paramVal, paramList, sampList) -from rrompy.utilities.base import purgeDict, verbosityDepth, getNewFilename +from rrompy.utilities.base import (purgeDict, verbosityManager as vbMng, + getNewFilename) from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, RROMPy_READY, RROMPy_FRAGILE) from rrompy.utilities.base import pickleDump, pickleLoad from rrompy.parameter import (emptyParameterList, checkParameter, checkParameterList) from rrompy.sampling import sampleList, emptySampleList __all__ = ['GenericApproximant'] def addNormFieldToClass(self, fieldName): - def objFunc(self, mu:paramList, homogeneized : bool = False) -> Np1D: + def objFunc(self, mu:paramList, *args, homogeneized : bool = False, + **kwargs) -> Np1D: uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized) - val = self.HFEngine.norm(uV) + val = self.HFEngine.norm(uV, *args, **kwargs) + return val + setattr(self.__class__, "norm" + fieldName, objFunc) + +def addNormDualFieldToClass(self, fieldName): + def objFunc(self, mu:paramList, *args, homogeneized : bool = False, + duality : bool = True, **kwargs) -> Np1D: + uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized, + duality) + val = self.HFEngine.norm(uV, *args, **kwargs) return val setattr(self.__class__, "norm" + fieldName, objFunc) def addPlotFieldToClass(self, fieldName): def objFunc(self, mu:paramList, *args, homogeneized : bool = False, **kwargs): uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized) kwargsCopy = copy(kwargs) for j, u in enumerate(uV): if "name" in kwargs.keys(): kwargsCopy["name"] = kwargs["name"] + str(j) self.HFEngine.plot(u, *args, **kwargs) setattr(self.__class__, "plot" + fieldName, objFunc) def addOutParaviewFieldToClass(self, fieldName): def objFunc(self, mu:paramVal, *args, homogeneized : bool = False, **kwargs): if not hasattr(self.HFEngine, "outParaview"): raise RROMPyException(("High fidelity engine cannot output to " "Paraview.")) uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized) kwargsCopy = copy(kwargs) for j, u in enumerate(uV): if "name" in kwargs.keys(): kwargsCopy["name"] = kwargs["name"] + str(j) self.HFEngine.outParaview(u, *args, **kwargsCopy) setattr(self.__class__, "outParaview" + fieldName, objFunc) def addOutParaviewTimeDomainFieldToClass(self, fieldName): def objFunc(self, mu:paramVal, *args, homogeneized : bool = False, **kwargs): if not hasattr(self.HFEngine, "outParaviewTimeDomain"): raise RROMPyException(("High fidelity engine cannot output to " "Paraview.")) uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized) omega = args.pop(0) if len(args) > 0 else np.real(mu) kwargsCopy = copy(kwargs) for j, u in enumerate(uV): if "name" in kwargs.keys(): kwargsCopy["name"] = kwargs["name"] + str(j) self.HFEngine.outParaviewTimeDomain(u, omega, *args, **kwargsCopy) setattr(self.__class__, "outParaviewTimeDomain" + fieldName, objFunc) class GenericApproximant: """ ABSTRACT ROM approximant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'S': total number of samples current approximant relies upon. Defaults to empty dict. homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults to False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. trainedModel: Trained model evaluator. mu0: Default parameter. homogeneized: Whether to homogeneize Dirichlet BCs. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList{Soft,Critical}. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon. verbosity: Verbosity level. POD: Whether to compute POD of snapshots. S: Number of solution snapshots over which current approximant is based upon. samplingEngine: Sampling engine. uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as sampleList. lastSolvedHF: Parameter(s) corresponding to last computed high fidelity solution(s) as parameterList. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApprox: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. """ __all__ += [ftype + dtype for ftype, dtype in iterprod( ["norm", "plot", "outParaview", "outParaviewTimeDomain"], ["HF", "RHS", "Approx", "Res", "Err"])] def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, approxParameters : DictAny = {}, 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) + vbMng(self, "INIT", + "Initializing engine of type {}.".format(self.name()), 10) self._HFEngine = HFEngine self.trainedModel = None self.lastSolvedHF = emptyParameterList() self.uHF = emptySampleList() self._addParametersToList(["POD"], [True], ["S"], [[1]]) if mu0 is None: if hasattr(self.HFEngine, "mu0"): self.mu0 = checkParameter(self.HFEngine.mu0) else: raise RROMPyException(("Center of approximation cannot be " "inferred from HF engine. Parameter " "required")) else: self.mu0 = checkParameter(mu0, self.HFEngine.npar) self.resetSamples() self.homogeneized = homogeneized self.approxParameters = approxParameters self._postInit() - ### add norm{HF,RHS,Approx,Res,Err} methods + ### add norm{HF,Approx,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", "Err"]: + for objName in ["HF", "Err"]: addNormFieldToClass(self, objName) - def objFunc(self, mu:paramList, homogeneized : bool = False) -> Np1D: -# uV = getattr(self.__class__, "getRes")(self, mu, homogeneized, -# duality = False) - uV = self.getRes(mu, homogeneized, duality = False) - val = self.HFEngine.norm(uV, dual = True, duality = False) - return val - setattr(self.__class__, "normRes", objFunc) if not hasattr(self, "normApprox"): addNormFieldToClass(self, "Approx") + ### add norm{RHS,Res} methods + """ + Compute norm of * at arbitrary parameter. + Args: + mu: Target parameter. + homogeneized(optional): Whether to remove Dirichlet BC. Defaults to + False. + duality(optional): Whether to compute duality of object. + Returns: + Target norm of *. + """ + for objName in ["RHS", "Res"]: + addNormDualFieldToClass(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 @property def parameterList(self): """Value of parameterListSoft + parameterListCritical.""" return self.parameterListSoft + self.parameterListCritical def _addParametersToList(self, whatSoft:strLst, defaultSoft:ListAny, whatCritical : strLst = [], defaultCritical : ListAny = [], toBeExcluded : strLst = []): if not hasattr(self, "parameterToBeExcluded"): self.parameterToBeExcluded = [] - self.parameterToBeExcluded += toBeExcluded + self.parameterToBeExcluded = toBeExcluded + self.parameterToBeExcluded if not hasattr(self, "parameterListSoft"): self.parameterListSoft = [] if not hasattr(self, "parameterDefaultSoft"): self.parameterDefaultSoft = {} if not hasattr(self, "parameterListCritical"): self.parameterListCritical = [] if not hasattr(self, "parameterDefaultCritical"): self.parameterDefaultCritical = {} for j, what in enumerate(whatSoft): if what not in self.parameterToBeExcluded: - self.parameterListSoft += [what] + self.parameterListSoft = [what] + self.parameterListSoft self.parameterDefaultSoft[what] = defaultSoft[j] for j, what in enumerate(whatCritical): if what not in self.parameterToBeExcluded: - self.parameterListCritical += [what] + self.parameterListCritical = ([what] + + self.parameterListCritical) self.parameterDefaultCritical[what] = defaultCritical[j] def _postInit(self): if self.depth == 0: - if self.verbosity >= 10: - verbosityDepth("DEL", "Done initializing.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done initializing.", 10) del self.depth else: self.depth -= 1 def name(self) -> str: return self.__class__.__name__ def __str__(self) -> str: return self.name() def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) def setupSampling(self): """Setup sampling engine.""" RROMPyAssert(self._mode, message = "Cannot setup sampling engine.") if not hasattr(self, "_POD") or self._POD is None: return if self.POD: - SamplingEngine = SamplingEngineLinearPOD + SamplingEngine = SamplingEngineStandardPOD else: - SamplingEngine = SamplingEngineLinear + SamplingEngine = SamplingEngineStandard self.samplingEngine = SamplingEngine(self.HFEngine, verbosity = self.verbosity, allowRepeatedSamples = True) @property def HFEngine(self): """Value of HFEngine.""" return self._HFEngine @HFEngine.setter def HFEngine(self, HFEngine): raise RROMPyException("Cannot change HFEngine.") @property def mu0(self): """Value of mu0.""" return self._mu0 @mu0.setter def mu0(self, mu0): mu0 = checkParameter(mu0) if not hasattr(self, "_mu0") or mu0 != self.mu0: self.resetSamples() self._mu0 = mu0 @property def npar(self): """Number of parameters.""" return self.mu0.shape[1] @property def approxParameters(self): """Value of approximant parameters.""" return self._approxParameters @approxParameters.setter def approxParameters(self, approxParams): if not hasattr(self, "approxParameters"): self._approxParameters = {} approxParameters = purgeDict(approxParams, self.parameterList, dictname = self.name() + ".approxParameters", baselevel = 1) keyList = list(approxParameters.keys()) for key in self.parameterListCritical: if key in keyList: setattr(self, "_" + key, self.parameterDefaultCritical[key]) for key in self.parameterListSoft: if key in keyList: setattr(self, "_" + key, self.parameterDefaultSoft[key]) fragile = False for key in self.parameterListCritical: if key in keyList: val = approxParameters[key] else: val = getattr(self, "_" + key, None) if val is None: val = self.parameterDefaultCritical[key] getattr(self.__class__, key, None).fset(self, val) fragile = fragile or val is None for key in self.parameterListSoft: if key in keyList: val = approxParameters[key] else: val = getattr(self, "_" + key, None) if val is None: val = self.parameterDefaultSoft[key] getattr(self.__class__, key, None).fset(self, val) if fragile: self._mode = RROMPy_FRAGILE @property def POD(self): """Value of POD.""" return self._POD @POD.setter def POD(self, POD): if hasattr(self, "_POD"): PODold = self.POD else: PODold = -1 self._POD = POD self._approxParameters["POD"] = self.POD if PODold != self.POD: self.samplingEngine = None self.resetSamples() @property def S(self): """Value of S.""" return self._S @S.setter def S(self, S): if not hasattr(S, "__len__"): S = [S] if any([s <= 0 for s in S]): raise RROMPyException("S must be positive.") if hasattr(self, "_S") and self._S is not None: Sold = tuple(self.S) else: Sold = -1 self._S = S self._approxParameters["S"] = self.S if Sold != tuple(self.S): self.resetSamples() @property def homogeneized(self): """Value of homogeneized.""" return self._homogeneized @homogeneized.setter def homogeneized(self, homogeneized): if not hasattr(self, "_homogeneized"): self._homogeneized = None if homogeneized != self.homogeneized: self._homogeneized = homogeneized self.resetSamples() @property def trainedModel(self): """Value of trainedModel.""" return self._trainedModel @trainedModel.setter def trainedModel(self, trainedModel): self._trainedModel = trainedModel if self._trainedModel is not None: self._trainedModel.lastSolvedApproxReduced = emptyParameterList() self._trainedModel.lastSolvedApprox = emptyParameterList() self.lastSolvedApproxReduced = emptyParameterList() self.lastSolvedApprox = emptyParameterList() self.uApproxReduced = emptySampleList() self.uApprox = emptySampleList() def resetSamples(self): if hasattr(self, "samplingEngine") and self.samplingEngine is not None: self.samplingEngine.resetHistory() else: self.setupSampling() self._mode = RROMPy_READY - def plotSamples(self, name : str = "u", save : str = None, - what : strLst = 'all', saveFormat : str = "eps", - saveDPI : int = 100, **figspecs): + def plotSamples(self, warping : List[callable] = None, name : str = "u", + save : str = None, what : strLst = 'all', + saveFormat : str = "eps", saveDPI : int = 100, + show : bool = True, plotArgs : dict = {}, **figspecs): """ Do some nice plots of the samples. Args: + warping(optional): Domain warping functions. name(optional): Name to be shown as title of the plots. Defaults to 'u'. what(optional): Which plots to do. If list, can contain 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. + show(optional): Whether to show figure. Defaults to True. + plotArgs(optional): Optional arguments for fen/pyplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ RROMPyAssert(self._mode, message = "Cannot plot samples.") - self.samplingEngine.plotSamples(name = name, save = save, what = what, - saveFormat = saveFormat, - saveDPI = saveDPI, - **figspecs) + self.samplingEngine.plotSamples(warping, name, save, what, saveFormat, + saveDPI, show, plotArgs, **figspecs) def outParaviewSamples(self, name : str = "u", filename : str = "out", times : Np1D = None, what : strLst = 'all', forceNewFile : bool = True, folders : bool = False, filePW = None): """ Output samples to ParaView file. Args: name(optional): Base name to be used for data output. filename(optional): Name of output file. times(optional): Timestamps. what(optional): Which plots to do. If list, can contain 'MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. forceNewFile(optional): Whether to create new output file. folders(optional): Whether to split output in folders. filePW(optional): Fenics File entity (for time series). """ RROMPyAssert(self._mode, message = "Cannot output samples.") self.samplingEngine.outParaviewSamples(name = name, filename = filename, times = times, what = what, forceNewFile = forceNewFile, folders = folders, filePW = filePW) def outParaviewTimeDomainSamples(self, omegas : Np1D = None, timeFinal : Np1D = None, periodResolution : int = 20, name : str = "u", filename : str = "out", forceNewFile : bool = True, folders : bool = False): """ Output samples to ParaView file, converted to time domain. Args: omegas(optional): frequencies. timeFinal(optional): final time of simulation. periodResolution(optional): number of time steps per period. name(optional): Base name to be used for data output. filename(optional): Name of output file. forceNewFile(optional): Whether to create new output file. folders(optional): Whether to split output in folders. """ RROMPyAssert(self._mode, message = "Cannot output samples.") self.samplingEngine.outParaviewTimeDomainSamples(omegas = omegas, timeFinal = timeFinal, periodResolution = periodResolution, name = name, filename = filename, forceNewFile = forceNewFile, folders = folders) def setSamples(self, samplingEngine): """Copy samplingEngine and samples.""" - if self.verbosity >= 10: - verbosityDepth("INIT", "Transfering samples.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Transfering samples.", 10) self.samplingEngine = copy(samplingEngine) - if self.verbosity >= 10: - verbosityDepth("DEL", "Done transfering samples.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done transfering samples.", 10) def setTrainedModel(self, model): """Deepcopy approximation from trained model.""" if hasattr(model, "storeTrainedModel"): verb = model.verbosity model.verbosity = 0 fileOut = model.storeTrainedModel() model.verbosity = verb else: try: fileOut = getNewFilename("trained_model", "pkl") pickleDump(model.data.__dict__, fileOut) except: raise RROMPyException(("Failed to store model data. Parameter " "model must have either " "storeTrainedModel or " "data.__dict__ properties.")) self.loadTrainedModel(fileOut) osrm(fileOut) @abstractmethod def setupApprox(self): """ Setup approximant. (ABSTRACT) Any specialization should include something like if self.checkComputedApprox(): return RROMPyAssert(self._mode, message = "Cannot setup approximant.") ... self.trainedModel = ... self.trainedModel.data = ... self.trainedModel.data.approxParameters = copy( self.approxParameters) """ pass def checkComputedApprox(self) -> bool: """ Check if setup of new approximant is not needed. Returns: True if new setup is not needed. False otherwise. """ return self._mode == RROMPy_FRAGILE or (self.trainedModel is not None and self.trainedModel.data.approxParameters == self.approxParameters) def _pruneBeforeEval(self, mu:paramList, field:str, append:bool, prune:bool) -> Tuple[paramList, Np1D, Np1D, bool]: - mu, _ = checkParameterList(mu, self.npar) + mu = checkParameterList(mu, self.npar)[0] idx = np.empty(len(mu), dtype = np.int) if prune: jExtra = np.zeros(len(mu), dtype = bool) muExtra = emptyParameterList() lastSolvedMus = getattr(self, "lastSolved" + field) if (len(mu) > 0 and len(mu) == len(lastSolvedMus) and mu == lastSolvedMus): idx = np.arange(len(mu), dtype = np.int) return muExtra, jExtra, idx, True muKeep = copy(muExtra) for j in range(len(mu)): jPos = lastSolvedMus.find(mu[j]) if jPos is not None: idx[j] = jPos muKeep.append(mu[j]) else: jExtra[j] = True muExtra.append(mu[j]) if len(muKeep) > 0 and not append: lastSolvedu = getattr(self, "u" + field) idx[~jExtra] = getattr(self.__class__, "set" + field)(self, muKeep, lastSolvedu[idx[~jExtra]], append) append = True else: jExtra = np.ones(len(mu), dtype = bool) muExtra = mu return muExtra, jExtra, idx, append def _setObject(self, mu:paramList, field:str, object:sampList, append:bool) -> List[int]: - newMus, _ = checkParameterList(mu, self.npar) + newMus = checkParameterList(mu, self.npar)[0] newObj = sampleList(object) if append: getattr(self, "lastSolved" + field).append(newMus) getattr(self, "u" + field).append(newObj) Ltot = len(getattr(self, "u" + field)) return list(range(Ltot - len(newObj), Ltot)) setattr(self, "lastSolved" + field, copy(newMus)) setattr(self, "u" + field, copy(newObj)) return list(range(len(getattr(self, "u" + field)))) def setHF(self, muHF:paramList, uHF:sampleList, append : bool = False) -> List[int]: """Assign high fidelity solution.""" return self._setObject(muHF, "HF", uHF, append) def evalHF(self, mu:paramList, append : bool = False, prune : bool = True) -> List[int]: """ Find high fidelity solution with original parameters and arbitrary parameter. Args: mu: Target parameter. append(optional): Whether to append new HF solutions to old ones. prune(optional): Whether to remove duplicates of already appearing HF solutions. """ muExtra, jExtra, idx, append = self._pruneBeforeEval(mu, "HF", append, prune) if len(muExtra) > 0: - newuHFs = self.samplingEngine.solveLS(muExtra, - homogeneized = self.homogeneized) + vbMng(self, "INIT", "Solving HF model for mu = {}.".format(mu), + 15) + newuHFs = self.HFEngine.solve(muExtra, + homogeneized = self.homogeneized) + vbMng(self, "DEL", "Done solving HF model.", 15) idx[jExtra] = self.setHF(muExtra, newuHFs, append) return list(idx) def setApproxReduced(self, muApproxR:paramList, uApproxR:sampleList, append : bool = False) -> List[int]: """Assign high fidelity solution.""" return self._setObject(muApproxR, "ApproxReduced", uApproxR, append) def evalApproxReduced(self, mu:paramList, append : bool = False, prune : bool = True) -> List[int]: """ Evaluate reduced representation of approximant at arbitrary parameter. Args: mu: Target parameter. append(optional): Whether to append new HF solutions to old ones. prune(optional): Whether to remove duplicates of already appearing HF solutions. """ self.setupApprox() muExtra, jExtra, idx, append = self._pruneBeforeEval(mu, "ApproxReduced", append, prune) if len(muExtra) > 0: newuApproxs = self.trainedModel.getApproxReduced(muExtra) idx[jExtra] = self.setApproxReduced(muExtra, newuApproxs, append) return list(idx) def setApprox(self, muApprox:paramList, uApprox:sampleList, append : bool = False) -> List[int]: """Assign high fidelity solution.""" return self._setObject(muApprox, "Approx", uApprox, append) def evalApprox(self, mu:paramList, append : bool = False, prune : bool = True) -> List[int]: """ Evaluate approximant at arbitrary parameter. Args: mu: Target parameter. append(optional): Whether to append new HF solutions to old ones. prune(optional): Whether to remove duplicates of already appearing HF solutions. """ self.setupApprox() muExtra, jExtra, idx, append = self._pruneBeforeEval(mu, "Approx", append, prune) if len(muExtra) > 0: newuApproxs = self.trainedModel.getApprox(muExtra) idx[jExtra] = self.setApprox(muExtra, newuApproxs, append) return list(idx) def getHF(self, mu:paramList, homogeneized : bool = False, append : bool = False, prune : bool = True) -> sampList: """ Get HF solution at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: HFsolution. """ - mu, _ = checkParameterList(mu, self.npar) + mu = checkParameterList(mu, self.npar)[0] idx = self.evalHF(mu, append = append, prune = prune) uHFs = self.uHF(idx) if self.homogeneized and not homogeneized: for j, m in enumerate(mu): uHFs[j] += self.HFEngine.liftDirichletData(m) if not self.homogeneized and homogeneized: for j, m in enumerate(mu): uHFs[j] -= self.HFEngine.liftDirichletData(m) return uHFs def getRHS(self, mu:paramList, homogeneized : bool = False, duality : bool = True) -> sampList: """ Get linear system RHS at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Linear system RHS. """ return self.HFEngine.residual(None, mu, homogeneized = homogeneized, duality = duality) def getApproxReduced(self, mu:paramList, append : bool = False, prune : bool = True) -> sampList: """ Get approximant at arbitrary parameter. Args: mu: Target parameter. Returns: Reduced approximant. """ - mu, _ = checkParameterList(mu, self.npar) + mu = checkParameterList(mu, self.npar)[0] idx = self.evalApproxReduced(mu, append = append, prune = prune) uApproxRs = self.uApproxReduced(idx) return uApproxRs def getApprox(self, mu:paramList, homogeneized : bool = False, append : bool = False, prune : bool = True) -> sampList: """ Get approximant at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Approximant. """ - mu, _ = checkParameterList(mu, self.npar) + mu = checkParameterList(mu, self.npar)[0] idx = self.evalApprox(mu, append = append, prune = prune) uApproxs = self.uApprox(idx) if self.homogeneized and not homogeneized: for j, m in enumerate(mu): uApproxs[j] += self.HFEngine.liftDirichletData(m) if not self.homogeneized and homogeneized: for j, m in enumerate(mu): uApproxs[j] -= self.HFEngine.liftDirichletData(m) return uApproxs def getRes(self, mu:paramList, homogeneized : bool = False, duality : bool = True) -> sampList: """ Get residual at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Approximant residual. """ return self.HFEngine.residual(self.getApprox(mu, homogeneized), mu, homogeneized = homogeneized, duality = duality) def getErr(self, mu:paramList, homogeneized : bool = False, append : bool = False, prune : bool = True) -> sampList: """ Get error at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Approximant error. """ return (self.getApprox(mu, homogeneized, append = append, prune =prune) - self.getHF(mu, homogeneized, append = append, prune = prune)) - def getPoles(self) -> Np1D: + def getPoles(self, *args, **kwargs) -> 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) + vbMng(self, "INIT", "Computing poles of model.", 20) + poles = self.trainedModel.getPoles(*args, **kwargs) + vbMng(self, "DEL", "Done computing poles.", 20) return poles def storeTrainedModel(self, filenameBase : str = "trained_model", forceNewFile : bool = True) -> str: """Store trained reduced model to file.""" self.setupApprox() - if self.verbosity >= 20: - verbosityDepth("INIT", "Storing trained model to file.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Storing trained model to file.", 20) if forceNewFile: filename = getNewFilename(filenameBase, "pkl") else: filename = "{}.pkl".format(filenameBase) pickleDump(self.trainedModel.data.__dict__, filename) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done storing trained model.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done storing trained model.", 20) 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) + vbMng(self, "INIT", "Loading pre-trained model from file.", 20) datadict = pickleLoad(filename) name = datadict.pop("name") - if name == "TrainedModelPade": + if name == "TrainedModelRational": from rrompy.reduction_methods.trained_model import \ - TrainedModelPade as tModel - elif name == "TrainedModelRB": + TrainedModelRational as tModel + elif name == "TrainedModelReducedBasis": from rrompy.reduction_methods.trained_model import \ - TrainedModelRB as tModel + TrainedModelReducedBasis as tModel else: raise RROMPyException(("Trained model name not recognized. " "Loading failed.")) self.mu0 = datadict.pop("mu0") from rrompy.reduction_methods.trained_model import TrainedModelData trainedModel = tModel() trainedModel.verbosity = self.verbosity trainedModel.timestamp = self.timestamp + self.scaleFactor = datadict.pop("scaleFactor") data = TrainedModelData(name, self.mu0, datadict.pop("projMat"), - datadict.pop("rescalingExp")) + self.scaleFactor, datadict.pop("rescalingExp")) if "mus" in datadict: data.mus = datadict.pop("mus") approxParameters = datadict.pop("approxParameters") data.approxParameters = copy(approxParameters) if "sampler" in approxParameters: self._approxParameters["sampler"] = approxParameters.pop("sampler") self.approxParameters = copy(approxParameters) if "mus" in data.__dict__: self.mus = copy(data.mus) - if name == "TrainedModelPade": - self.scaleFactor = datadict.pop("scaleFactor") - data.scaleFactor = self.scaleFactor for key in datadict: setattr(data, key, datadict[key]) trainedModel.data = data self.trainedModel = trainedModel self._mode = RROMPy_FRAGILE - if self.verbosity >= 20: - verbosityDepth("DEL", "Done loading pre-trained model.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done loading pre-trained model.", 20) diff --git a/rrompy/reduction_methods/base/pade_utils.py b/rrompy/reduction_methods/base/rational_interpolant_utils.py similarity index 78% rename from rrompy/reduction_methods/base/pade_utils.py rename to rrompy/reduction_methods/base/rational_interpolant_utils.py index d4bcfa6..b28956f 100644 --- a/rrompy/reduction_methods/base/pade_utils.py +++ b/rrompy/reduction_methods/base/rational_interpolant_utils.py @@ -1,35 +1,32 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.utilities.base.types import Np1D from rrompy.utilities.exception_manager import RROMPyWarning __all__ = ['checkRobustTolerance'] -def checkRobustTolerance(ev:Np1D, N:int, tol:float) -> dict: +def checkRobustTolerance(ev:Np1D, tol:float) -> dict: """ Perform robustness check on eigen-/singular values and return reduced parameters with warning. """ ts = tol * np.linalg.norm(ev) - if len(ev) <= np.sum(np.abs(ev) >= ts) + 1: return {} - RROMPyWarning(("Smallest {} eigenvalues below tolerance. Reducing N by " - "1.").format(len(ev) - np.sum(np.abs(ev) >= ts))) - return {"N" : N - 1} + return len(ev) - np.sum(np.abs(ev) >= ts) diff --git a/rrompy/reduction_methods/base/rb_utils.py b/rrompy/reduction_methods/base/reduced_basis_utils.py similarity index 63% rename from rrompy/reduction_methods/base/rb_utils.py rename to rrompy/reduction_methods/base/reduced_basis_utils.py index 78b37cc..fde59e0 100644 --- a/rrompy/reduction_methods/base/rb_utils.py +++ b/rrompy/reduction_methods/base/reduced_basis_utils.py @@ -1,62 +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 rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, sampList +from rrompy.utilities.base.types import (Np1D, Np2D, Tuple, List, ListAny, + sampList) +from rrompy.utilities.poly_fitting.polynomial import ( + hashIdxToDerivative as hashI) from rrompy.utilities.exception_manager import RROMPyAssert from rrompy.sampling import sampleList __all__ = ['projectAffineDecomposition'] def projectAffineDecomposition(As:List[Np2D], bs:List[Np1D], pMat:sampList, ARBsOld : List[Np2D] = None, bRBsOld : List[Np1D] = None, pMatOld : sampList = None)\ -> Tuple[List[Np2D], List[Np1D]]: """Project affine decomposition of linear system onto basis.""" RROMPyAssert((ARBsOld is None, bRBsOld is None), (pMatOld is None, pMatOld is None), "Old affine projected terms") if isinstance(pMat, (sampleList,)): pMat = pMat.data pMatH = pMat.T.conj() ARBs = [None] * len(As) bRBs = [None] * len(bs) if pMatOld is None: - for j in range(len(As)): - ARBs[j] = pMatH.dot(As[j].dot(pMat)) - for j in range(len(bs)): - bRBs[j] = pMatH.dot(bs[j]) + for j in range(max(len(As), len(bs))): + if j < len(As): + ARBs[j] = pMatH.dot(As[j].dot(pMat)) + if j < len(bs): + bRBs[j] = pMatH.dot(bs[j]) else: RROMPyAssert((len(ARBsOld), len(bRBsOld)), (len(As), len(bs)), "Old affine projected terms") if isinstance(pMatOld, (sampleList,)): pMatOld = pMatOld.data pMatOldH = pMatOld.T.conj() Sold = pMatOld.shape[1] Snew = pMat.shape[1] - for j in range(len(As)): - ARBs[j] = np.empty((Sold + Snew, Sold + Snew), dtype = np.complex) - ARBs[j][: Sold, : Sold] = ARBsOld[j] - ARBs[j][: Sold, Sold :] = pMatOldH.dot(As[j].dot(pMat)) - ARBs[j][Sold :, : Sold] = pMatH.dot(As[j].dot(pMatOld)) - ARBs[j][Sold :, Sold :] = pMatH.dot(As[j].dot(pMat)) - for j in range(len(bs)): - bRBs[j] = np.empty((Sold + Snew), dtype = np.complex) - bRBs[j][: Sold] = bRBsOld[j] - bRBs[j][Sold :] = pMatH.dot(bs[j]) + for j in range(max(len(As), len(bs))): + if j < len(As): + ARBs[j] = np.empty((Sold + Snew, Sold + Snew), + dtype = np.complex) + ARBs[j][: Sold, : Sold] = ARBsOld[j] + ARBs[j][: Sold, Sold :] = pMatOldH.dot(As[j].dot(pMat)) + ARBs[j][Sold :, : Sold] = pMatH.dot(As[j].dot(pMatOld)) + ARBs[j][Sold :, Sold :] = pMatH.dot(As[j].dot(pMat)) + if j < len(bs): + bRBs[j] = np.empty((Sold + Snew), dtype = np.complex) + bRBs[j][: Sold] = bRBsOld[j] + bRBs[j][Sold :] = pMatH.dot(bs[j]) return ARBs, bRBs diff --git a/rrompy/reduction_methods/centered/generic_centered_approximant.py b/rrompy/reduction_methods/centered/generic_centered_approximant.py deleted file mode 100644 index aea1c71..0000000 --- a/rrompy/reduction_methods/centered/generic_centered_approximant.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -import numpy as np -from rrompy.reduction_methods.base.generic_approximant import ( - GenericApproximant) -from rrompy.utilities.base.types import paramList -from rrompy.utilities.base import verbosityDepth -from rrompy.utilities.exception_manager import RROMPyAssert - -__all__ = ['GenericCenteredApproximant'] - -class GenericCenteredApproximant(GenericApproximant): - """ - ROM single-point approximant computation for parametric problems - (ABSTRACT). - - Args: - HFEngine: HF problem solver. - mu0(optional): Default parameter. Defaults to 0. - approxParameters(optional): Dictionary containing values for main - parameters of approximant. Recognized keys are: - - 'POD': whether to compute POD of snapshots; defaults to True; - - 'S': total number of samples current approximant relies upon. - Defaults to empty dict. - homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults - to False. - verbosity(optional): Verbosity level. Defaults to 10. - - Attributes: - HFEngine: HF problem solver. - mu0: Default parameter. - homogeneized: Whether to homogeneize Dirichlet BCs. - approxParameters: Dictionary containing values for main parameters of - approximant. Recognized keys are in parameterList. - parameterListSoft: Recognized keys of soft approximant parameters: - - 'POD': whether to compute POD of snapshots. - parameterListCritical: Recognized keys of critical approximant - parameters: - - 'S': total number of samples current approximant relies upon. - POD: Whether to compute QR factorization of derivatives. - S: Number of solution snapshots over which current approximant is - based upon. - initialHFData: HF problem initial data. - samplingEngine: Sampling engine. - uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as - sampleList. - lastSolvedHF: Parameter(s) corresponding to last computed high fidelity - solution(s) as parameterList. - uApproxReduced: Reduced approximate solution(s) with parameter(s) - lastSolvedApprox as sampleList. - lastSolvedApproxReduced: Parameter(s) corresponding to last computed - reduced approximate solution(s) as parameterList. - uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as - sampleList. - lastSolvedApprox: Parameter(s) corresponding to last computed - approximate solution(s) as parameterList. - """ - - @property - def S(self): - """Value of S.""" - return self._S - @S.setter - def S(self, S): - GenericApproximant.S.fset(self, S) - RROMPyAssert(len(self.S), 1, "Length of S") - - def computeDerivatives(self): - """Compute derivatives of solution map starting from order 0.""" - RROMPyAssert(self._mode, - message = "Cannot start derivative computation.") - if self.samplingEngine.nsamples != np.prod(self.S): - if self.verbosity >= 5: - verbosityDepth("INIT", "Starting computation of derivatives.", - timestamp = self.timestamp) - self.samplingEngine.iterSample([self.mu0[0]] * np.prod(self.S), - homogeneized = self.homogeneized) - if self.verbosity >= 5: - verbosityDepth("DEL", "Done computing derivatives.", - timestamp = self.timestamp) - - def normApprox(self, mu:paramList, homogeneized : bool = False) -> float: - """ - Compute norm of approximant at arbitrary parameter. - - Args: - mu: Target parameter. - homogeneized(optional): Whether to remove Dirichlet BC. Defaults to - False. - - Returns: - Target norm of approximant. - """ - if not self.POD or self.homogeneized != homogeneized: - return super().normApprox(mu, homogeneized) - return np.linalg.norm(self.getApproxReduced(mu).data, axis = 0) - diff --git a/rrompy/reduction_methods/centered/rational_pade.py b/rrompy/reduction_methods/centered/rational_pade.py deleted file mode 100644 index d02a3e6..0000000 --- a/rrompy/reduction_methods/centered/rational_pade.py +++ /dev/null @@ -1,443 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -from copy import deepcopy as copy -import numpy as np -from rrompy.reduction_methods.base import checkRobustTolerance -from rrompy.reduction_methods.trained_model import (TrainedModelData, - TrainedModelPade as tModel) -from .generic_centered_approximant import GenericCenteredApproximant -from rrompy.utilities.base.types import (Np1D, Np2D, Tuple, DictAny, HFEng, - paramVal, paramList, sampList) -from rrompy.utilities.base import verbosityDepth -from rrompy.utilities.poly_fitting.polynomial import (nextDerivativeIndices, - hashDerivativeToIdx as hashD, - hashIdxToDerivative as hashI, - homogeneizedToFull) -from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, - RROMPyWarning) - -__all__ = ['RationalPade'] - -class RationalPade(GenericCenteredApproximant): - """ - ROM single-point fast Pade' approximant computation for parametric - problems. - - Args: - HFEngine: HF problem solver. - mu0(optional): Default parameter. Defaults to 0. - approxParameters(optional): Dictionary containing values for main - parameters of approximant. Recognized keys are: - - 'POD': whether to compute POD of snapshots; defaults to True; - - 'S': total number of samples current approximant relies upon; - - 'E': number of derivatives used to compute Pade'; defaults to 0; - - 'M': degree of Pade' approximant numerator; defaults to 0; - - 'N': degree of Pade' approximant denominator; defaults to 0; - - 'robustTol': tolerance for robust Pade' denominator management; - defaults to 0. - Defaults to empty dict. - homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults - to False. - verbosity(optional): Verbosity level. Defaults to 10. - - Attributes: - HFEngine: HF problem solver. - mu0: Default parameter. - homogeneized: Whether to homogeneize Dirichlet BCs. - approxParameters: Dictionary containing values for main parameters of - approximant. Recognized keys are in parameterList. - parameterListSoft: Recognized keys of soft approximant parameters: - - 'POD': whether to compute POD of snapshots; - - 'E': number of derivatives used to compute Pade'; - - 'M': degree of Pade' approximant numerator; - - 'N': degree of Pade' approximant denominator; - - 'robustTol': tolerance for robust Pade' denominator management. - parameterListCritical: Recognized keys of critical approximant - parameters: - - 'S': total number of samples current approximant relies upon. - POD: Whether to compute QR factorization of derivatives. - S: Number of solution snapshots over which current approximant is - based upon. - M: Numerator degree of approximant. - N: Denominator degree of approximant. - robustTol: Tolerance for robust Pade' denominator management. - E: Complete derivative depth level of S. - uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as - sampleList. - lastSolvedHF: Parameter(s) corresponding to last computed high fidelity - solution(s) as parameterList. - uApproxReduced: Reduced approximate solution(s) with parameter(s) - lastSolvedApprox as sampleList. - lastSolvedApproxReduced: Parameter(s) corresponding to last computed - reduced approximate solution(s) as parameterList. - uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as - sampleList. - lastSolvedApprox: Parameter(s) corresponding to last computed - approximate solution(s) as parameterList. - G: Square Numpy 2D vector of size (N+1) corresponding to Pade' - denominator matrix (see paper). - """ - - def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, - approxParameters : DictAny = {}, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): - self._preInit() - self._addParametersToList(["E", "M", "N", "robustTol"], [-1, 0, 0, 0]) - super().__init__(HFEngine = HFEngine, mu0 = mu0, - approxParameters = approxParameters, - homogeneized = homogeneized, - verbosity = verbosity, timestamp = timestamp) - self._postInit() - - @property - def M(self): - """Value of M..""" - return self._M - @M.setter - def M(self, M): - if M < 0: raise RROMPyException("M must be non-negative.") - self._M = M - self._approxParameters["M"] = self.M - if hasattr(self, "_E") and self.E >= 0 and self.E < self.M: - RROMPyWarning("Prescribed E is too small. Decreasing M.") - self.M = self.E - - @property - def N(self): - """Value of N.""" - return self._N - @N.setter - def N(self, N): - if N < 0: raise RROMPyException("N must be non-negative.") - self._N = N - self._approxParameters["N"] = self.N - if hasattr(self, "_E") and self.E >= 0 and self.E < self.N: - RROMPyWarning("Prescribed E is too small. Decreasing N.") - self.N = self.E - - @property - def E(self): - """Value of E.""" - return self._E - @E.setter - def E(self, E): - if E < 0: - if not hasattr(self, "_S"): - raise RROMPyException(("Value of E must be positive if S is " - "not yet assigned.")) - E = np.sum(hashI(np.prod(self.S), self.npar)) - 1 - self._E = E - self._approxParameters["E"] = self.E - if (hasattr(self, "_S") - and self.E >= np.sum(hashI(np.prod(self.S), self.npar))): - RROMPyWarning("Prescribed S is too small. Decreasing E.") - self.E = -1 - if hasattr(self, "_M"): self.M = self.M - if hasattr(self, "_N"): self.N = self.N - - @property - def robustTol(self): - """Value of tolerance for robust Pade' denominator management.""" - return self._robustTol - @robustTol.setter - def robustTol(self, robustTol): - if robustTol < 0.: - RROMPyWarning(("Overriding prescribed negative robustness " - "tolerance to 0.")) - robustTol = 0. - self._robustTol = robustTol - self._approxParameters["robustTol"] = self.robustTol - - @property - def S(self): - """Value of S.""" - return self._S - @S.setter - def S(self, S): - GenericCenteredApproximant.S.fset(self, S) - if hasattr(self, "_M"): self.M = self.M - if hasattr(self, "_N"): self.N = self.N - if hasattr(self, "_E"): self.E = self.E - - def _setupDenominator(self): - """Compute Pade' denominator.""" - RROMPyAssert(self._mode, message = "Cannot setup denominator.") - if self.verbosity >= 7: - verbosityDepth("INIT", "Starting computation of denominator.", - timestamp = self.timestamp) - while self.N > 0: - if self.POD: - ev, eV = self.findeveVGQR() - else: - ev, eV = self.findeveVGExplicit() - newParameters = checkRobustTolerance(ev, self.N, self.robustTol) - if not newParameters: - break - self.approxParameters = newParameters - if self.N <= 0: - eV = np.ones((1, 1)) - q = homogeneizedToFull(tuple([self.N + 1] * self.npar), self.npar, - eV[:, 0]) - if self.verbosity >= 7: - verbosityDepth("DEL", "Done computing denominator.", - timestamp = self.timestamp) - return q - - def _setupNumerator(self): - """Compute Pade' numerator.""" - RROMPyAssert(self._mode, message = "Cannot setup numerator.") - if self.verbosity >= 7: - verbosityDepth("INIT", "Starting computation of numerator.", - timestamp = self.timestamp) - P = np.zeros(tuple([self.M + 1] * self.npar) + (np.prod(self.S),), - dtype = np.complex) - mEnd = hashD([self.M + 1] + [0] * (self.npar - 1)) - nEnd = hashD([self.N + 1] + [0] * (self.npar - 1)) - mnIdxs = nextDerivativeIndices([], self.npar, max(mEnd, nEnd)) - for j in range(mEnd): - mIdx = mnIdxs[j] - for n in range(nEnd): - diffIdx = [x - y for (x, y) in zip(mIdx, mnIdxs[n])] - if all([x >= 0 for x in diffIdx]): - P[tuple(mIdx) + (hashD(diffIdx),)] = ( - self.trainedModel.data.Q[tuple(mnIdxs[n])]) - Pr = self.rescaleByParameter(P) - if self.POD: - Pr = np.tensordot(Pr, self.samplingEngine.RPOD.T, - axes = ([-1], [0])) - if self.verbosity >= 7: - verbosityDepth("DEL", "Done computation numerator.", - timestamp = self.timestamp) - return Pr - - def setupApprox(self): - """ - Compute Pade' approximant. SVD-based robust eigenvalue management. - """ - if self.checkComputedApprox(): - return - RROMPyAssert(self._mode, message = "Cannot setup approximant.") - if self.verbosity >= 5: - verbosityDepth("INIT", "Setting up {}.". format(self.name()), - timestamp = self.timestamp) - self.computeDerivatives() - if self.trainedModel is None: - self.trainedModel = tModel() - self.trainedModel.verbosity = self.verbosity - self.trainedModel.timestamp = self.timestamp - data = TrainedModelData(self.trainedModel.name(), self.mu0, - None, self.HFEngine.rescalingExp) - data.polytype = "MONOMIAL" - data.polytypeP = "MONOMIAL" - self.trainedModel.data = data - else: - self.trainedModel = self.trainedModel - if self.N > 0: - Q = self._setupDenominator() - else: - self.setScaleParameter() - Q = np.ones(tuple([1] * self.npar), dtype = np.complex) - self.trainedModel.data.Q = copy(Q) - self.trainedModel.data.scaleFactor = self.scaleFactor - self.trainedModel.data.projMat = copy(self.samplingEngine.samples( - list(range(np.prod(self.S))))) - self.trainedModel.data.P = copy(self._setupNumerator()) - self.trainedModel.data.approxParameters = copy(self.approxParameters) - if self.verbosity >= 5: - verbosityDepth("DEL", "Done setting up approximant.", - timestamp = self.timestamp) - - def setScaleParameter(self) -> Np2D: - """Compute parameter for rescaling.""" - RROMPyAssert(self._mode, message = "Cannot compute rescaling factor.") - self.computeDerivatives() - self.scaleFactor = [1.] * self.npar - for d in range(self.npar): - hashesd = [0] - for n in range(1, self.E + 1): - hashesd += [hashD([0] * (d - 1) + [n] - + [0] * (self.npar - d - 1))] - if self.POD: - Rd = self.samplingEngine.RPOD[: hashesd[-1] + 1, hashesd] - Gd = np.diag(Rd.T.conj().dot(Rd)) - else: - DerEd = self.samplingEngine.samples(hashesd) - Gd = self.HFEngine.norm(DerEd) - if len(Gd) > 1: - scaleCoeffs = np.polyfit(np.arange(len(Gd)), np.log(Gd), 1) - self.scaleFactor[d] = np.exp(- scaleCoeffs[0] / 2.) - - def rescaleByParameter(self, R:Np2D) -> Np2D: - """ - Rescale by scale parameter. - - Args: - R: Matrix whose columns need rescaling. - - Returns: - Rescaled matrix. - """ - RIdxs = nextDerivativeIndices([], self.npar, R.shape[-1]) - Rscaled = copy(R) - for j, RIdx in enumerate(RIdxs): - Rscaled[..., j] *= np.prod([x ** y for (x, y) in - zip(self.scaleFactor, RIdx)]) - return Rscaled - - def buildG(self): - """Assemble Pade' denominator matrix.""" - RROMPyAssert(self._mode, message = "Cannot compute G matrix.") - self.computeDerivatives() - if self.verbosity >= 10: - verbosityDepth("INIT", "Building gramian matrix.", - timestamp = self.timestamp) - eStart = hashD([self.E] + [0] * (self.npar - 1)) - eEnd = hashD([self.E + 1] + [0] * (self.npar - 1)) - eIdxs = [hashI(e, self.npar) for e in range(eStart, eEnd)] - nEnd = hashD([self.N + 1] + [0] * (self.npar - 1)) - nIdxs = nextDerivativeIndices([], self.npar, nEnd) - self.setScaleParameter() - if self.POD: - RPODE = self.rescaleByParameter(self.samplingEngine.RPOD[: eEnd, - : eEnd]) - else: - DerE = self.rescaleByParameter(self.samplingEngine.samples( - list(range(eEnd))).data) - self.G = np.zeros((nEnd, nEnd), dtype = np.complex) - for eIdx in eIdxs: - nLoc = [] - samplesIdxs = [] - for n, nIdx in enumerate(nIdxs): - diffIdx = [x - y for (x, y) in zip(eIdx, nIdx)] - if all([x >= 0 for x in diffIdx]): - nLoc += [n] - samplesIdxs += [hashD(diffIdx)] - if self.POD: - RPODELoc = RPODE[: samplesIdxs[-1] + 1, samplesIdxs] - GLoc = RPODELoc.T.conj().dot(RPODELoc) - else: - DerELoc = DerE[:, samplesIdxs] - GLoc = self.HFEngine.innerProduct(DerELoc, DerELoc) - for j in range(len(nLoc)): - self.G[nLoc[j], nLoc] = self.G[nLoc[j], nLoc] + GLoc[j] - if self.verbosity >= 10: - verbosityDepth("DEL", "Done building gramian.", - timestamp = self.timestamp) - - def findeveVGExplicit(self) -> Tuple[Np1D, Np2D]: - """ - Compute explicitly eigenvalues and eigenvectors of Pade' denominator - matrix. - """ - RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.") - self.buildG() - if self.verbosity >= 7: - verbosityDepth("INIT", - "Solving eigenvalue problem for gramian matrix.", - timestamp = self.timestamp) - ev, eV = np.linalg.eigh(self.G) - if self.verbosity >= 5: - try: condev = ev[-1] / ev[0] - except: condev = np.inf - verbosityDepth("MAIN", ("Solved eigenvalue problem of size {} " - "with condition number {:.4e}.").format( - self.G.shape[0], - condev), - timestamp = self.timestamp) - if self.verbosity >= 7: - verbosityDepth("DEL", "Done solving eigenvalue problem.", - timestamp = self.timestamp) - return ev, eV - - def findeveVGQR(self) -> Tuple[Np1D, Np2D]: - """ - Compute eigenvalues and eigenvectors of Pade' denominator matrix - through SVD of R factor. - - Returns: - Eigenvalues in ascending order and corresponding eigenvector - matrix. - """ - RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.") - RROMPyAssert(self.POD, True, "POD value") - self.computeDerivatives() - eStart = hashD([self.E] + [0] * (self.npar - 1)) - eEnd = hashD([self.E + 1] + [0] * (self.npar - 1)) - eIdxs = [hashI(e, self.npar) for e in range(eStart, eEnd)] - nEnd = hashD([self.N + 1] + [0] * (self.npar - 1)) - nIdxs = nextDerivativeIndices([], self.npar, nEnd) - self.setScaleParameter() - RPODE = self.rescaleByParameter(self.samplingEngine.RPOD[: eEnd, - : eEnd]) - Rstack = np.zeros((RPODE.shape[0] * (eEnd - eStart), nEnd), - dtype = np.complex) - for k, eIdx in enumerate(eIdxs): - nLoc = [] - samplesIdxs = [] - for n, nIdx in enumerate(nIdxs): - diffIdx = [x - y for (x, y) in zip(eIdx, nIdx)] - if all([x >= 0 for x in diffIdx]): - nLoc += [n] - samplesIdxs += [hashD(diffIdx)] - RPODELoc = RPODE[:, samplesIdxs] - for j in range(len(nLoc)): - Rstack[k * RPODE.shape[0] : (k + 1) * RPODE.shape[0], - nLoc[j]] = RPODELoc[:, j] - if self.verbosity >= 7: - verbosityDepth("INIT", ("Solving svd for square root of " - "gramian matrix."), - timestamp = self.timestamp) - sizeI = Rstack.shape - _, s, V = np.linalg.svd(Rstack, full_matrices = False) - eV = V[::-1, :].T.conj() - if self.verbosity >= 5: - try: condev = s[0] / s[-1] - except: condev = np.inf - verbosityDepth("MAIN", ("Solved svd problem of size {} x {} with " - "condition number {:.4e}.").format(*sizeI, - condev), - timestamp = self.timestamp) - if self.verbosity >= 7: - verbosityDepth("DEL", "Done solving eigenvalue problem.", - timestamp = self.timestamp) - return s[::-1], eV - - def centerNormalize(self, mu : paramList = [], - mu0 : paramVal = None) -> paramList: - """ - Compute normalized parameter to be plugged into approximant. - - Args: - mu: Parameter(s) 1. - mu0: Parameter(s) 2. If None, set to self.mu0. - - Returns: - Normalized parameter. - """ - return self.trainedModel.centerNormalize(mu, mu0) - - def getResidues(self) -> sampList: - """ - Obtain approximant residues. - - Returns: - Matrix with residues as columns. - """ - return self.trainedModel.getResidues() - diff --git a/rrompy/reduction_methods/centered/rb_centered.py b/rrompy/reduction_methods/centered/rb_centered.py deleted file mode 100644 index a646478..0000000 --- a/rrompy/reduction_methods/centered/rb_centered.py +++ /dev/null @@ -1,222 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -from copy import deepcopy as copy -import numpy as np -from .generic_centered_approximant import GenericCenteredApproximant -from rrompy.reduction_methods.trained_model import TrainedModelRB as tModel -from rrompy.reduction_methods.trained_model import TrainedModelData -from rrompy.reduction_methods.base.rb_utils import projectAffineDecomposition -from rrompy.utilities.base.types import (Np1D, Np2D, Tuple, List, DictAny, - HFEng, paramVal, sampList) -from rrompy.utilities.base import verbosityDepth -from rrompy.utilities.exception_manager import (RROMPyException, RROMPyWarning, - RROMPyAssert) - -__all__ = ['RBCentered'] - -class RBCentered(GenericCenteredApproximant): - """ - ROM single-point fast RB approximant computation for parametric problems - with polynomial dependence up to degree 2. - - Args: - HFEngine: HF problem solver. - mu0(optional): Default parameter. Defaults to 0. - approxParameters(optional): Dictionary containing values for main - parameters of approximant. Recognized keys are: - - 'POD': whether to compute POD of snapshots; defaults to True; - - 'S': total number of samples current approximant relies upon; - - 'R': rank for Galerkin projection; defaults to prod(S); - - 'PODTolerance': tolerance for snapshots POD; defaults to -1. - Defaults to empty dict. - homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults - to False. - verbosity(optional): Verbosity level. Defaults to 10. - - Attributes: - HFEngine: HF problem solver. - mu0: Default parameter. - homogeneized: Whether to homogeneize Dirichlet BCs. - approxParameters: Dictionary containing values for main parameters of - approximant. Recognized keys are in parameterList. - parameterListSoft: Recognized keys of soft approximant parameters: - - 'POD': whether to compute POD of snapshots; - - 'R': rank for Galerkin projection; - - 'PODTolerance': tolerance for snapshots POD. - parameterListCritical: Recognized keys of critical approximant - parameters: - - 'S': total number of samples current approximant relies upon. - POD: Whether to compute QR factorization of derivatives. - R: Rank for Galerkin projection. - PODTolerance: Tolerance for snapshots POD. - S: Number of solution snapshots over which current approximant is - based upon. - uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as - sampleList. - lastSolvedHF: Parameter(s) corresponding to last computed high fidelity - solution(s) as parameterList. - uApproxReduced: Reduced approximate solution(s) with parameter(s) - lastSolvedApprox as sampleList. - lastSolvedApproxReduced: Parameter(s) corresponding to last computed - reduced approximate solution(s) as parameterList. - uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as - sampleList. - lastSolvedApprox: Parameter(s) corresponding to last computed - approximate solution(s) as parameterList. - As: List of sparse matrices (in CSC format) representing coefficients - of linear system matrix wrt theta(mu). - bs: List of numpy vectors representing coefficients of linear system - RHS wrt theta(mu). - thetaAs: List of callables representing coefficients of linear system - matrix wrt mu. - thetabs: List of callables representing coefficients of linear system - RHS wrt mu. - ARBs: List of sparse matrices (in CSC format) representing coefficients - of compressed linear system matrix wrt theta(mu). - bRBs: List of numpy vectors representing coefficients of compressed - linear system RHS wrt theta(mu). - """ - - def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, - approxParameters : DictAny = {}, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): - self._preInit() - self._addParametersToList(["R", "PODTolerance"], [1, -1]) - super().__init__(HFEngine = HFEngine, mu0 = mu0, - approxParameters = approxParameters, - homogeneized = homogeneized, - verbosity = verbosity, timestamp = timestamp) - if self.verbosity >= 10: - verbosityDepth("INIT", "Computing affine blocks of system.", - timestamp = self.timestamp) - self.As = self.HFEngine.affineLinearSystemA(self.mu0) - self.bs = self.HFEngine.affineLinearSystemb(self.mu0, - self.homogeneized) - if self.verbosity >= 10: - verbosityDepth("DEL", "Done computing affine blocks.", - timestamp = self.timestamp) - self._postInit() - - @property - def S(self): - """Value of S.""" - return self._S - @S.setter - def S(self, S): - GenericCenteredApproximant.S.fset(self, S) - if not hasattr(self, "_R"): self._R = np.prod(self.S) - - @property - def R(self): - """Value of R. Its assignment may change S.""" - return self._R - @R.setter - def R(self, R): - if R < 0: raise RROMPyException("R must be non-negative.") - self._R = R - self._approxParameters["R"] = self.R - if hasattr(self, "_S") and np.prod(self.S) < self.R: - RROMPyWarning("Prescribed S is too small. Decreasing R.") - self.R = np.prod(self.S) - - @property - def PODTolerance(self): - """Value of PODTolerance.""" - return self._PODTolerance - @PODTolerance.setter - def PODTolerance(self, PODTolerance): - self._PODTolerance = PODTolerance - self._approxParameters["PODTolerance"] = self.PODTolerance - - def setupApprox(self): - """Setup RB system.""" - if self.checkComputedApprox(): - return - RROMPyAssert(self._mode, message = "Cannot setup approximant.") - if self.verbosity >= 5: - verbosityDepth("INIT", "Setting up {}.". format(self.name()), - timestamp = self.timestamp) - self.computeDerivatives() - if self.verbosity >= 7: - verbosityDepth("INIT", "Computing projection matrix.", - timestamp = self.timestamp) - if self.POD: - U, s, _ = np.linalg.svd(self.samplingEngine.RPOD) - s = s ** 2. - else: - Gramian = self.HFEngine.innerProduct(self.samplingEngine.samples, - self.samplingEngine.samples) - U, s, _ = np.linalg.svd(Gramian) - nsamples = self.samplingEngine.nsamples - snorm = np.cumsum(s[::-1]) / np.sum(s) - nPODTrunc = min(nsamples - np.argmax(snorm > self.PODTolerance), - self.R) - pMat = self.samplingEngine.samples.dot(U[:, : nPODTrunc]) - if self.verbosity >= 5: - verbosityDepth("MAIN", ("Assembling {}x{} projection matrix from " - "{} samples.").format(*(pMat.shape), - nsamples), - timestamp = self.timestamp) - - if self.trainedModel is None: - self.trainedModel = tModel() - self.trainedModel.verbosity = self.verbosity - self.trainedModel.timestamp = self.timestamp - data = TrainedModelData(self.trainedModel.name(), self.mu0, - pMat, self.HFEngine.rescalingExp) - data.thetaAs = self.HFEngine.affineWeightsA(self.mu0) - data.thetabs = self.HFEngine.affineWeightsb(self.mu0, - self.homogeneized) - self.trainedModel.data = data - else: - self.trainedModel = self.trainedModel - self.trainedModel.data.projMat = copy(pMat) - ARBs, bRBs = self.assembleReducedSystem(pMat) - self.trainedModel.data.ARBs = ARBs - self.trainedModel.data.bRBs = bRBs - - if self.verbosity >= 7: - verbosityDepth("DEL", "Done computing projection matrix.", - timestamp = self.timestamp) - self.trainedModel.data.approxParameters = copy(self.approxParameters) - if self.verbosity >= 5: - verbosityDepth("DEL", "Done setting up approximant.", - timestamp = self.timestamp) - - def assembleReducedSystem(self, pMat : sampList = None, - pMatOld : sampList = None)\ - -> Tuple[List[Np2D], List[Np1D]]: - """Build affine blocks of RB linear system through projections.""" - if pMat is None: - self.setupApprox() - ARBs = self.trainedModel.data.ARBs - bRBs = self.trainedModel.data.bRBs - else: - if self.verbosity >= 10: - verbosityDepth("INIT", "Projecting affine terms of HF model.", - timestamp = self.timestamp) - ARBsOld = None if pMatOld is None else self.trainedModel.data.ARBs - bRBsOld = None if pMatOld is None else self.trainedModel.data.bRBs - ARBs, bRBs = projectAffineDecomposition(self.As, self.bs, pMat, - ARBsOld, bRBsOld, pMatOld) - if self.verbosity >= 10: - verbosityDepth("DEL", "Done projecting affine terms.", - timestamp = self.timestamp) - return ARBs, bRBs - diff --git a/rrompy/reduction_methods/distributed_greedy/__init__.py b/rrompy/reduction_methods/greedy/__init__.py similarity index 76% rename from rrompy/reduction_methods/distributed_greedy/__init__.py rename to rrompy/reduction_methods/greedy/__init__.py index ba0008b..623f762 100644 --- a/rrompy/reduction_methods/distributed_greedy/__init__.py +++ b/rrompy/reduction_methods/greedy/__init__.py @@ -1,30 +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_distributed_greedy_approximant import \ - GenericDistributedGreedyApproximant +from .generic_greedy_approximant import GenericGreedyApproximant from .rational_interpolant_greedy import RationalInterpolantGreedy -from .rb_distributed_greedy import RBDistributedGreedy +from .reduced_basis_greedy import ReducedBasisGreedy __all__ = [ - 'GenericDistributedGreedyApproximant', + 'GenericGreedyApproximant', 'RationalInterpolantGreedy', - 'RBDistributedGreedy' + 'ReducedBasisGreedy' ] diff --git a/rrompy/reduction_methods/distributed_greedy/generic_distributed_greedy_approximant.py b/rrompy/reduction_methods/greedy/generic_greedy_approximant.py similarity index 83% rename from rrompy/reduction_methods/distributed_greedy/generic_distributed_greedy_approximant.py rename to rrompy/reduction_methods/greedy/generic_greedy_approximant.py index 9817e7c..c0f41fa 100644 --- a/rrompy/reduction_methods/distributed_greedy/generic_distributed_greedy_approximant.py +++ b/rrompy/reduction_methods/greedy/generic_greedy_approximant.py @@ -1,597 +1,583 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import deepcopy as copy import numpy as np -from rrompy.reduction_methods.distributed.generic_distributed_approximant \ - import GenericDistributedApproximant +from rrompy.reduction_methods.standard.generic_standard_approximant \ + import GenericStandardApproximant from rrompy.utilities.base.types import (Np1D, Np2D, DictAny, HFEng, Tuple, List, normEng, paramVal, paramList, sampList) -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.solver import normEngine from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, RROMPyWarning) from rrompy.parameter import checkParameterList, emptyParameterList -__all__ = ['GenericDistributedGreedyApproximant'] +__all__ = ['GenericGreedyApproximant'] def pruneSamples(mus:paramList, badmus:paramList, tol : float = 1e-8) -> paramList: """Remove from mus all the elements which are too close to badmus.""" if len(badmus) == 0: return mus musNp = np.array(mus(0)) badmus = np.array(badmus(0)) proximity = np.min(np.abs(musNp.reshape(-1, 1) - np.tile(badmus.reshape(1, -1), [len(mus), 1])), axis = 1).flatten() idxPop = np.arange(len(mus))[proximity <= tol] for i, j in enumerate(idxPop): mus.pop(j - i) return mus -class GenericDistributedGreedyApproximant(GenericDistributedApproximant): +class GenericGreedyApproximant(GenericStandardApproximant): """ ROM greedy interpolant computation for parametric problems (ABSTRACT). Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'S': number of starting training points; - 'sampler': sample point generator; - 'greedyTol': uniform error tolerance for greedy algorithm; defaults to 1e-2; - 'interactive': whether to interactively terminate greedy algorithm; defaults to False; - 'maxIter': maximum number of greedy steps; defaults to 1e2; - 'refinementRatio': ratio of test points to be exhausted before test set refinement; defaults to 0.2; - 'nTestPoints': number of test points; defaults to 5e2; - - 'trainSetGenerator': training sample points generator. + - 'trainSetGenerator': training sample points generator; defaults + to sampler. Defaults to empty dict. homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults to False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. homogeneized: Whether to homogeneize Dirichlet BCs. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. - 'greedyTol': uniform error tolerance for greedy algorithm; - 'interactive': whether to interactively terminate greedy algorithm; - 'maxIter': maximum number of greedy steps; - 'refinementRatio': ratio of test points to be exhausted before test set refinement; - - 'nTestPoints': number of test points. + - 'nTestPoints': number of test points; + - 'trainSetGenerator': training sample points generator. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon; - - 'sampler': sample point generator; - - 'trainSetGenerator': training sample points generator. + - 'sampler': sample point generator. POD: whether to compute POD of snapshots. S: number of test points. sampler: Sample point generator. greedyTol: uniform error tolerance for greedy algorithm. interactive: whether to interactively terminate greedy algorithm. maxIter: maximum number of greedy steps. refinementRatio: ratio of training points to be exhausted before training set refinement. nTestPoints: number of starting training points. trainSetGenerator: training sample points generator. muBounds: list of bounds for parameter values. samplingEngine: Sampling engine. estimatorNormEngine: Engine for estimator norm computation. uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as sampleList. lastSolvedHF: Parameter(s) corresponding to last computed high fidelity solution(s) as parameterList. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApprox: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. """ TOL_INSTABILITY = 1e-6 def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() - from rrompy.parameter.parameter_sampling import QuadratureSampler as QS self._addParametersToList(["greedyTol", "interactive", "maxIter", "refinementRatio", "nTestPoints"], [1e-2, False, 1e2, .2, 5e2], - ["trainSetGenerator"], - [QS([[0], [1]], "UNIFORM")]) - del QS + ["trainSetGenerator"], ["AUTO"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) RROMPyAssert(self.HFEngine.npar, 1, "Parameter dimension") self._postInit() @property def greedyTol(self): """Value of greedyTol.""" return self._greedyTol @greedyTol.setter def greedyTol(self, greedyTol): if greedyTol < 0: raise RROMPyException("greedyTol must be non-negative.") if hasattr(self, "_greedyTol") and self.greedyTol is not None: greedyTolold = self.greedyTol else: greedyTolold = -1 self._greedyTol = greedyTol self._approxParameters["greedyTol"] = self.greedyTol if greedyTolold != self.greedyTol: self.resetSamples() @property def interactive(self): """Value of interactive.""" return self._interactive @interactive.setter def interactive(self, interactive): self._interactive = interactive @property def maxIter(self): """Value of maxIter.""" return self._maxIter @maxIter.setter def maxIter(self, maxIter): if maxIter <= 0: raise RROMPyException("maxIter must be positive.") if hasattr(self, "_maxIter") and self.maxIter is not None: maxIterold = self.maxIter else: maxIterold = -1 self._maxIter = maxIter self._approxParameters["maxIter"] = self.maxIter if maxIterold != self.maxIter: self.resetSamples() @property def refinementRatio(self): """Value of refinementRatio.""" return self._refinementRatio @refinementRatio.setter def refinementRatio(self, refinementRatio): if refinementRatio <= 0. or refinementRatio > 1.: raise RROMPyException(("refinementRatio must be between 0 " "(excluded) and 1.")) if (hasattr(self, "_refinementRatio") and self.refinementRatio is not None): refinementRatioold = self.refinementRatio else: refinementRatioold = -1 self._refinementRatio = refinementRatio self._approxParameters["refinementRatio"] = self.refinementRatio if refinementRatioold != self.refinementRatio: self.resetSamples() @property def nTestPoints(self): """Value of nTestPoints.""" return self._nTestPoints @nTestPoints.setter def nTestPoints(self, nTestPoints): if nTestPoints <= 0: raise RROMPyException("nTestPoints must be positive.") if not np.isclose(nTestPoints, np.int(nTestPoints)): raise RROMPyException("nTestPoints must be an integer.") nTestPoints = np.int(nTestPoints) if hasattr(self, "_nTestPoints") and self.nTestPoints is not None: nTestPointsold = self.nTestPoints else: nTestPointsold = -1 self._nTestPoints = nTestPoints self._approxParameters["nTestPoints"] = self.nTestPoints if nTestPointsold != self.nTestPoints: self.resetSamples() @property def trainSetGenerator(self): """Value of trainSetGenerator.""" return self._trainSetGenerator @trainSetGenerator.setter def trainSetGenerator(self, trainSetGenerator): + if (isinstance(trainSetGenerator, (str,)) + and trainSetGenerator.upper() == "AUTO"): + trainSetGenerator = self.sampler if 'generatePoints' not in dir(trainSetGenerator): raise RROMPyException("trainSetGenerator type not recognized.") if (hasattr(self, '_trainSetGenerator') - and self.trainSetGenerator is not None): + and self.trainSetGenerator not in [None, "AUTO"]): trainSetGeneratorOld = self.trainSetGenerator self._trainSetGenerator = trainSetGenerator self._approxParameters["trainSetGenerator"] = self.trainSetGenerator if (not 'trainSetGeneratorOld' in locals() or trainSetGeneratorOld != self.trainSetGenerator): self.resetSamples() def resetSamples(self): """Reset samples.""" super().resetSamples() self._mus = emptyParameterList() def initEstimatorNormEngine(self, normEngn : normEng = None): """Initialize estimator norm engine.""" if (normEngn is not None or not hasattr(self, "estimatorNormEngine") or self.estimatorNormEngine is None): if normEngn is None: if not hasattr(self.HFEngine, "energyNormPartialDualMatrix"): self.HFEngine.buildEnergyNormPartialDualForm() estimatorEnergyMatrix = ( self.HFEngine.energyNormPartialDualMatrix) else: if hasattr(normEngn, "buildEnergyNormPartialDualForm"): if not hasattr(normEngn, "energyNormPartialDualMatrix"): normEngn.buildEnergyNormPartialDualForm() estimatorEnergyMatrix = ( normEngn.energyNormPartialDualMatrix) else: estimatorEnergyMatrix = normEngn self.estimatorNormEngine = normEngine(estimatorEnergyMatrix) def errorEstimator(self, mus:paramList) -> List[complex]: """ Standard residual-based error estimator with explicit residual computation. """ self.setupApprox() + res = self.getRes(mus, homogeneized = self.homogeneized, + duality = False) if self.HFEngine.nbs == 1: RHS = self.getRHS(mus[0], homogeneized = self.homogeneized, duality = False) - RHSNorm = self.estimatorNormEngine.norm(RHS) - res = self.getRes(mus, homogeneized = self.homogeneized, - duality = False) - err = self.estimatorNormEngine.norm(res) / RHSNorm else: - res = self.getRes(mus, homogeneized = self.homogeneized, - duality = False) RHS = self.getRHS(mus, homogeneized = self.homogeneized, duality = False) - err = (self.estimatorNormEngine.norm(res) - / self.estimatorNormEngine.norm(RHS)) - return np.abs(err) + return np.abs(self.estimatorNormEngine.norm(res) + / self.estimatorNormEngine.norm(RHS)) def getMaxErrorEstimator(self, mus:paramList, plot : bool = False) -> Tuple[Np1D, int, float]: """ Compute maximum of (and index of maximum of) error estimator over given parameters. """ errorEstTest = self.errorEstimator(mus) idxMaxEst = np.argmax(errorEstTest) maxEst = errorEstTest[idxMaxEst] if plot and not np.all(np.isinf(errorEstTest)): musre = mus.re(0) from matplotlib import pyplot as plt plt.figure() plt.semilogy(musre, errorEstTest, 'k') plt.semilogy([musre[0], musre[-1]], [self.greedyTol] * 2, 'r--') plt.semilogy(self.mus.re(0), 2. * self.greedyTol * np.ones(len(self.mus)), '*m') plt.semilogy(musre[idxMaxEst], maxEst, 'xr') plt.grid() plt.show() plt.close() return errorEstTest, idxMaxEst, maxEst def greedyNextSample(self, muidx:int, plotEst : bool = False)\ -> Tuple[Np1D, int, float, paramVal]: """Compute next greedy snapshot of solution map.""" RROMPyAssert(self._mode, message = "Cannot add greedy sample.") mu = copy(self.muTest[muidx]) self.muTest.pop(muidx) - if self.verbosity >= 2: - verbosityDepth("MAIN", ("Adding {}-th sample point at {} to " - "training set.").format( - self.samplingEngine.nsamples + 1, mu), - timestamp = self.timestamp) + vbMng(self, "MAIN", + ("Adding {}-th sample point at {} to training " + "set.").format(self.samplingEngine.nsamples + 1, mu), 2) self.mus.append(mu) self.samplingEngine.nextSample(mu, homogeneized = self.homogeneized) errorEstTest, muidx, maxErrorEst = self.getMaxErrorEstimator( self.muTest, plotEst) return errorEstTest, muidx, maxErrorEst, self.muTest[muidx] def _preliminaryTraining(self): """Initialize starting snapshots of solution map.""" RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.") self.computeScaleFactor() if self.samplingEngine.nsamples > 0: return - if self.verbosity >= 2: - verbosityDepth("INIT", "Starting computation of snapshots.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Starting computation of snapshots.", 2) self.resetSamples() self.mus = self.trainSetGenerator.generatePoints(self.S) + muTestBase = self.sampler.generatePoints(self.nTestPoints) + muTestBase = pruneSamples(muTestBase, self.mus, + 1e-10 * self.scaleFactor[0]).sort() muLast = copy(self.mus[-1]) self.mus.pop() - muTestBase = self.sampler.generatePoints(self.nTestPoints) if len(self.mus) > 0: - if self.verbosity >= 2: - verbosityDepth("MAIN", - ("Adding first {} samples point at {} to " - "training set.").format(np.prod(self.S) - 1, - self.mus), - timestamp = self.timestamp) + nSamples = np.prod(self.S) - 1 + vbMng(self, "MAIN", + ("Adding first {} sample point{} at {} to training " + "set.").format(nSamples, "" + "s" * (nSamples > 1), + self.mus), 2) self.samplingEngine.iterSample(self.mus, homogeneized = self.homogeneized) - muTestBase = pruneSamples(muTestBase, self.mus, - 1e-10 * self.scaleFactor[0]).sort() self.muTest = emptyParameterList() self.muTest.reset((len(muTestBase) + 1, self.mus.shape[1])) self.muTest[: -1] = muTestBase self.muTest[-1] = muLast def _enrichTestSet(self, nTest:int): """Add extra elements to test set.""" RROMPyAssert(self._mode, message = "Cannot enrich test set.") muTestExtra = self.sampler.generatePoints(2 * nTest) muTotal = copy(self.mus) muTotal.append(self.muTest) muTestExtra = pruneSamples(muTestExtra, muTotal, 1e-10 * self.scaleFactor[0]) muTestNew = np.empty(len(self.muTest) + len(muTestExtra), dtype = np.complex) muTestNew[: len(self.muTest)] = self.muTest(0) muTestNew[len(self.muTest) :] = muTestExtra(0) - self.muTest = checkParameterList(muTestNew.sort(), self.npar) - if self.verbosity >= 5: - verbosityDepth("MAIN", "Enriching test set by {} elements.".format( - len(muTestExtra)), - timestamp = self.timestamp) + self.muTest = checkParameterList(muTestNew.sort(), self.npar)[0] + vbMng(self, "MAIN", + "Enriching test set by {} elements.".format(len(muTestExtra)), 5) def greedy(self, plotEst : bool = False): """Compute greedy snapshots of solution map.""" RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.") if self.samplingEngine.nsamples > 0: return 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) + vbMng(self, "MAIN", + "Uniform testing error estimate {:.4e}.".format(maxErrorEst), 2) 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) + vbMng(self, "MAIN", + "Uniform testing error estimate {:.4e}.".format(maxErrorEst), + 2) 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 = "") + vbMng(self, "MAIN", + ("Required tolerance {} achieved. Want to decrease " + "greedyTol and continue? Y/N").format(self.greedyTol), + 0, end = "") increasemaxIter = input() if increasemaxIter.upper() == "Y": - verbosityDepth("MAIN", "Reducing value of greedyTol...", - timestamp = self.timestamp) + vbMng(self, "MAIN", "Reducing value of greedyTol...", 0) 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 = "") + vbMng(self, "MAIN", + ("Maximum number of iterations {} reached. Want to " + "increase maxIter and continue? " + "Y/N").format(self.maxIter), 0, end = "") increasemaxIter = input() if increasemaxIter.upper() == "Y": - verbosityDepth("MAIN", "Doubling value of maxIter...", - timestamp = self.timestamp) + vbMng(self, "MAIN", "Doubling value of maxIter...", 0) self._maxIter *= 2 - if self.verbosity >= 2: - verbosityDepth("DEL", ("Done computing snapshots (final snapshot " - "count: {}).").format( - self.samplingEngine.nsamples), - timestamp = self.timestamp) + vbMng(self, "DEL", + ("Done computing snapshots (final snapshot count: " + "{}).").format(self.samplingEngine.nsamples), 2) def checkComputedApprox(self) -> bool: """ Check if setup of new approximant is not needed. Returns: True if new setup is not needed. False otherwise. """ return (super().checkComputedApprox() and len(self.mus) == self.trainedModel.data.projMat.shape[1]) def assembleReducedResidualGramian(self, pMat:sampList): """ Build residual gramian of reduced linear system through projections. """ self.initEstimatorNormEngine() if (not hasattr(self.trainedModel.data, "gramian") or self.trainedModel.data.gramian is None): gramian = self.estimatorNormEngine.innerProduct(pMat, pMat) else: Sold = self.trainedModel.data.gramian.shape[0] S = len(self.mus) if Sold > S: gramian = self.trainedModel.data.gramian[: S, : S] else: idxOld = list(range(Sold)) idxNew = list(range(Sold, S)) gramian = np.empty((S, S), dtype = np.complex) gramian[: Sold, : Sold] = self.trainedModel.data.gramian gramian[: Sold, Sold :] = ( self.estimatorNormEngine.innerProduct(pMat(idxNew), pMat(idxOld))) gramian[Sold :, : Sold] = gramian[: Sold, Sold :].T.conj() gramian[Sold :, Sold :] = ( self.estimatorNormEngine.innerProduct(pMat(idxNew), pMat(idxNew))) self.trainedModel.data.gramian = gramian - def assembleReducedResidualBlocksbb(self, bs:List[Np1D], - scaling : float = 1.): + def assembleReducedResidualBlocksbb(self, bs:List[Np1D]): """ Build blocks (of type bb) of reduced linear system through projections. """ self.initEstimatorNormEngine() nbs = len(bs) if (not hasattr(self.trainedModel.data, "resbb") or self.trainedModel.data.resbb is None): resbb = np.empty((nbs, nbs), dtype = np.complex) for i in range(nbs): - Mbi = scaling ** i * bs[i] + Mbi = bs[i] resbb[i, i] = self.estimatorNormEngine.innerProduct(Mbi, Mbi) for j in range(i): - Mbj = scaling ** j * bs[j] + Mbj = bs[j] resbb[i, j] = self.estimatorNormEngine.innerProduct(Mbj, Mbi) for i in range(nbs): for j in range(i + 1, nbs): resbb[i, j] = resbb[j, i].conj() self.trainedModel.data.resbb = resbb def assembleReducedResidualBlocksAb(self, As:List[Np2D], bs:List[Np1D], - pMat:sampList, scaling : float = 1.): + pMat:sampList): """ Build blocks (of type Ab) of reduced linear system through projections. """ self.initEstimatorNormEngine() nAs = len(As) nbs = len(bs) S = len(self.mus) if (not hasattr(self.trainedModel.data, "resAb") or self.trainedModel.data.resAb is None): if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data resAb = np.empty((nbs, S, nAs), dtype = np.complex) for j in range(nAs): - MAj = scaling ** (j + 1) * As[j].dot(pMat) + MAj = As[j].dot(pMat) for i in range(nbs): - Mbi = scaling ** (i + 1) * bs[i] + Mbi = bs[i] resAb[i, :, j] = self.estimatorNormEngine.innerProduct(MAj, Mbi) else: Sold = self.trainedModel.data.resAb.shape[1] if Sold == S: return if Sold > S: resAb = self.trainedModel.data.resAb[:, : S, :] else: if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data resAb = np.empty((nbs, S, nAs), dtype = np.complex) resAb[:, : Sold, :] = self.trainedModel.data.resAb for j in range(nAs): - MAj = scaling ** (j + 1) * As[j].dot(pMat[:, Sold :]) + MAj = As[j].dot(pMat[:, Sold :]) for i in range(nbs): - Mbi = scaling ** (i + 1) * bs[i] + Mbi = bs[i] resAb[i, Sold :, j] = ( self.estimatorNormEngine.innerProduct(MAj, Mbi)) self.trainedModel.data.resAb = resAb - def assembleReducedResidualBlocksAA(self, As:List[Np2D], pMat:sampList, - scaling : float = 1.): + def assembleReducedResidualBlocksAA(self, As:List[Np2D], pMat:sampList): """ Build blocks (of type AA) of reduced linear system through projections. """ self.initEstimatorNormEngine() nAs = len(As) S = len(self.mus) if (not hasattr(self.trainedModel.data, "resAA") or self.trainedModel.data.resAA is None): if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data resAA = np.empty((S, nAs, S, nAs), dtype = np.complex) for i in range(nAs): - MAi = scaling ** (i + 1) * As[i].dot(pMat) + MAi = As[i].dot(pMat) resAA[:, i, :, i] = ( self.estimatorNormEngine.innerProduct(MAi, MAi)) for j in range(i): - MAj = scaling ** (j + 1) * As[j].dot(pMat) + MAj = As[j].dot(pMat) resAA[:, i, :, j] = ( self.estimatorNormEngine.innerProduct(MAj, MAi)) for i in range(nAs): for j in range(i + 1, nAs): resAA[:, i, :, j] = resAA[:, j, :, i].T.conj() else: Sold = self.trainedModel.data.resAA.shape[0] if Sold == S: return if Sold > S: resAA = self.trainedModel.data.resAA[: S, :, : S, :] else: if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data resAA = np.empty((S, nAs, S, nAs), dtype = np.complex) resAA[: Sold, :, : Sold, :] = self.trainedModel.data.resAA for i in range(nAs): - MAi = scaling ** (i + 1) * As[i].dot(pMat) + MAi = As[i].dot(pMat) resAA[: Sold, i, Sold :, i] = ( self.estimatorNormEngine.innerProduct(MAi[:, Sold :], MAi[:, : Sold])) resAA[Sold :, i, : Sold, i] = resAA[: Sold, i, Sold :, i].T.conj() resAA[Sold :, i, Sold :, i] = ( self.estimatorNormEngine.innerProduct(MAi[:, Sold :], MAi[:, Sold :])) for j in range(i): - MAj = scaling ** (j + 1) * As[j].dot(pMat) + MAj = As[j].dot(pMat) resAA[: Sold, i, Sold :, j] = ( self.estimatorNormEngine.innerProduct(MAj[:, Sold :], MAi[:, : Sold])) resAA[Sold :, i, : Sold, j] = ( self.estimatorNormEngine.innerProduct(MAj[:, : Sold], MAi[:, Sold :])) resAA[Sold :, i, Sold :, j] = ( self.estimatorNormEngine.innerProduct(MAj[:, Sold :], MAi[:, Sold :])) for i in range(nAs): for j in range(i + 1, nAs): resAA[: Sold, i, Sold :, j] = ( resAA[Sold :, j, : Sold, i].T.conj()) resAA[Sold :, i, : Sold, j] = ( resAA[: Sold, j, Sold :, i].T.conj()) resAA[Sold :, i, Sold :, j] = ( resAA[Sold :, j, Sold :, i].T.conj()) self.trainedModel.data.resAA = resAA + def assembleReducedResidualBlocks(self, full : bool = False): + """Build affine blocks of affine decomposition of residual.""" + self.assembleReducedResidualBlocksbb(self.bs) + if full: + pMat = self.trainedModel.data.projMat + self.assembleReducedResidualBlocksAb(self.As, self.bs, pMat) + self.assembleReducedResidualBlocksAA(self.As, pMat) diff --git a/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py b/rrompy/reduction_methods/greedy/rational_interpolant_greedy.py similarity index 60% rename from rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py rename to rrompy/reduction_methods/greedy/rational_interpolant_greedy.py index 6a697a6..4ff7253 100644 --- a/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py +++ b/rrompy/reduction_methods/greedy/rational_interpolant_greedy.py @@ -1,426 +1,353 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import deepcopy as copy import numpy as np -from .generic_distributed_greedy_approximant import \ - GenericDistributedGreedyApproximant -from rrompy.utilities.poly_fitting.polynomial import polybases, polydomcoeff -from rrompy.reduction_methods.distributed import RationalInterpolant -from rrompy.reduction_methods.trained_model import TrainedModelPade as tModel +from .generic_greedy_approximant import GenericGreedyApproximant +from rrompy.utilities.poly_fitting.polynomial import (polybases, polydomcoeff, + PolynomialInterpolator as PI) +from rrompy.reduction_methods.standard import RationalInterpolant +from rrompy.reduction_methods.trained_model import ( + TrainedModelRational as tModel) from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.utilities.base.types import Np1D, Np2D, DictAny, HFEng, paramVal -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.exception_manager import RROMPyWarning from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert __all__ = ['RationalInterpolantGreedy'] -class RationalInterpolantGreedy(GenericDistributedGreedyApproximant, - RationalInterpolant): +class RationalInterpolantGreedy(GenericGreedyApproximant, RationalInterpolant): """ ROM greedy rational interpolant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'S': number of starting training points; - 'sampler': sample point generator; - 'radialBasis': radial basis family for interpolant numerator; defaults to 0, i.e. no radial basis; - 'radialBasisWeights': radial basis weights for interpolant numerator; defaults to 0, i.e. identity; - 'greedyTol': uniform error tolerance for greedy algorithm; defaults to 1e-2; - 'interactive': whether to interactively terminate greedy algorithm; defaults to False; - 'maxIter': maximum number of greedy steps; defaults to 1e2; - 'refinementRatio': ratio of training points to be exhausted before training set refinement; defaults to 0.2; - 'nTestPoints': number of test points; defaults to 5e2; - 'trainSetGenerator': training sample points generator; defaults - to Chebyshev sampler within muBounds; + to sampler; - 'polybasis': type of basis for interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults to 'MONOMIAL'; - - 'Delta': difference between M and N in rational approximant; - defaults to 0; - 'errorEstimatorKind': kind of error estimator; available values include 'EXACT', 'BASIC', and 'BARE'; defaults to 'EXACT'; - 'interpRcond': tolerance for interpolation; defaults to None; - - 'robustTol': tolerance for robust Pade' denominator management; - defaults to 0. + - 'robustTol': tolerance for robust rational denominator + management; defaults to 0. Defaults to empty dict. homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults to False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. homogeneized: Whether to homogeneize Dirichlet BCs. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. - 'radialBasis': radial basis family for interpolant numerator; defaults to 0, i.e. no radial basis; - 'radialBasisWeights': radial basis weights for interpolant numerator; defaults to 0, i.e. identity; - 'greedyTol': uniform error tolerance for greedy algorithm; - 'interactive': whether to interactively terminate greedy algorithm; - 'maxIter': maximum number of greedy steps; - 'refinementRatio': ratio of test points to be exhausted before test set refinement; - 'nTestPoints': number of test points; - 'trainSetGenerator': training sample points generator; - - 'Delta': difference between M and N in rational approximant; - 'errorEstimatorKind': kind of error estimator; - 'interpRcond': tolerance for interpolation; - - 'robustTol': tolerance for robust Pade' denominator management. + - 'robustTol': tolerance for robust rational denominator + management. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator. POD: whether to compute POD of snapshots. S: number of test points. sampler: Sample point generator. radialBasis: Radial basis family for interpolant numerator. radialBasisWeights: Radial basis weights for interpolant numerator. greedyTol: uniform error tolerance for greedy algorithm. interactive: whether to interactively terminate greedy algorithm. maxIter: maximum number of greedy steps. refinementRatio: ratio of training points to be exhausted before training set refinement. nTestPoints: number of starting training points. trainSetGenerator: training sample points generator. - robustTol: tolerance for robust Pade' denominator management. - Delta: difference between M and N in rational approximant. + robustTol: tolerance for robust rational denominator management. errorEstimatorKind: kind of error estimator. interpRcond: tolerance for interpolation. - robustTol: tolerance for robust Pade' denominator management. + robustTol: tolerance for robust rational denominator management. muBounds: list of bounds for parameter values. samplingEngine: Sampling engine. estimatorNormEngine: Engine for estimator norm computation. uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as sampleList. lastSolvedHF: Parameter(s) corresponding to last computed high fidelity solution(s) as parameterList. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApprox: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. """ _allowedEstimatorKinds = ["EXACT", "BASIC", "BARE"] def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() - self._addParametersToList(["Delta", "polybasis", "errorEstimatorKind", + self._addParametersToList(["polybasis", "errorEstimatorKind", "interpRcond", "robustTol"], - [0, "MONOMIAL", "EXACT", -1, 0], + ["MONOMIAL", "EXACT", -1, 0], toBeExcluded = ["E"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) - if self.verbosity >= 7: - verbosityDepth("INIT", "Computing Taylor blocks of system.", - timestamp = self.timestamp) - nAs = self.HFEngine.nAs - 1 - nbs = max(self.HFEngine.nbs, (nAs + 1) * self.homogeneized) - self.As = [self.HFEngine.A(self.mu0, j + 1) for j in range(nAs)] - self.bs = [self.HFEngine.b(self.mu0, j, self.homogeneized) - for j in range(nbs)] - if self.verbosity >= 7: - verbosityDepth("DEL", "Done computing Taylor blocks.", - timestamp = self.timestamp) self._postInit() + @property + def E(self): + """Value of E.""" + self._E = np.prod(self._S) - 1 + return self._E + @E.setter + def E(self, E): + RROMPyWarning(("E is used just to simplify inheritance, and its value " + "cannot be changed from that of prod(S) - 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("Sample type not recognized.") self._polybasis = polybasis except: RROMPyWarning(("Prescribed polybasis not recognized. Overriding " "to 'MONOMIAL'.")) self._polybasis = "MONOMIAL" self._approxParameters["polybasis"] = self.polybasis - @property - def Delta(self): - """Value of Delta.""" - return self._Delta - @Delta.setter - def Delta(self, Delta): - if not np.isclose(Delta, np.floor(Delta)): - raise RROMPyException("Delta must be an integer.") - if Delta < 0: - RROMPyWarning(("Error estimator unreliable for Delta < 0. " - "Overloading of errorEstimator is suggested.")) - else: - Deltamin = (max(self.HFEngine.nbs, - self.HFEngine.nAs * self.homogeneized) - - 1 - 1 * (self.HFEngine.nAs > 1)) - if Delta < Deltamin: - RROMPyWarning(("Method may be unreliable for selected Delta. " - "Suggested minimal value of Delta: {}.").format( - Deltamin)) - self._Delta = Delta - self._approxParameters["Delta"] = self.Delta - @property def errorEstimatorKind(self): """Value of errorEstimatorKind.""" return self._errorEstimatorKind @errorEstimatorKind.setter def errorEstimatorKind(self, errorEstimatorKind): errorEstimatorKind = errorEstimatorKind.upper() if errorEstimatorKind not in self._allowedEstimatorKinds: RROMPyWarning(("Error estimator kind not recognized. Overriding " "to 'EXACT'.")) errorEstimatorKind = "EXACT" self._errorEstimatorKind = errorEstimatorKind self._approxParameters["errorEstimatorKind"] = self.errorEstimatorKind @property def nTestPoints(self): """Value of nTestPoints.""" return self._nTestPoints @nTestPoints.setter def nTestPoints(self, nTestPoints): - if nTestPoints <= np.abs(self.Delta): - RROMPyWarning(("nTestPoints must be at least abs(Delta) + 1. " - "Increasing value to abs(Delta) + 1.")) - nTestPoints = np.abs(self.Delta) + 1 + if nTestPoints <= 0: + RROMPyWarning("nTestPoints must be at least 1. Overriding to 1.") + nTestPoints = 1 if not np.isclose(nTestPoints, np.int(nTestPoints)): raise RROMPyException("nTestPoints must be an integer.") nTestPoints = np.int(nTestPoints) if hasattr(self, "_nTestPoints") and self.nTestPoints is not None: nTestPointsold = self.nTestPoints else: nTestPointsold = -1 self._nTestPoints = nTestPoints self._approxParameters["nTestPoints"] = self.nTestPoints if nTestPointsold != self.nTestPoints: self.resetSamples() def _errorSamplingRatio(self, mus:Np1D, muRTest:Np1D, muRTrain:Np1D) -> Np1D: """Scalar ratio in explicit error estimator.""" self.setupApprox() testTile = np.tile(np.reshape(muRTest, (-1, 1)), [1, len(muRTrain)]) nodalVals = np.prod(testTile - np.reshape(muRTrain, (1, -1)), axis = 1) denVals = self.trainedModel.getQVal(mus) return np.abs(nodalVals / denVals) def _RHSNorms(self, radiusb0:Np2D) -> Np1D: """High fidelity system RHS norms.""" self.assembleReducedResidualBlocks(full = False) # 'ij,jk,ik->k', resbb, radiusb0, radiusb0.conj() b0resb0 = np.sum(self.trainedModel.data.resbb.dot(radiusb0) * radiusb0.conj(), axis = 0) RHSnorms = np.power(np.abs(b0resb0), .5) return RHSnorms def _errorEstimatorBare(self) -> Np1D: """Bare residual-based error estimator.""" self.setupApprox() self.assembleReducedResidualGramian(self.trainedModel.data.projMat) - pDom = self.trainedModel.data.P[-1, :] + pDom = self.trainedModel.data.P.domCoeff LL = pDom.conj().dot(self.trainedModel.data.gramian.dot(pDom)) Adiag = self.As[0].diagonal() - LL = ((self.scaleFactor[0] * np.linalg.norm(Adiag)) ** 2. * LL - / np.size(Adiag)) - scalingDom = polydomcoeff(len(self.mus) - 1, self.polybasis) - return scalingDom * np.power(np.abs(LL), .5) + AnormApprox = np.linalg.norm(Adiag) ** 2. / np.size(Adiag) + return np.abs(AnormApprox * LL) ** .5 def _errorEstimatorBasic(self, muTest:paramVal, ratioTest:complex) -> Np1D: """Basic residual-based error estimator.""" - resmu = self.HFEngine.residual(self.trainedModel.getApprox(muTest), - muTest, self.homogeneized, - duality = False)[0] + resmu = self.HFEngine.residual(self.getApprox(muTest), muTest, + self.homogeneized, duality = False)[0] return np.abs(self.estimatorNormEngine.norm(resmu) / ratioTest) def _errorEstimatorExact(self, muRTrain:Np1D, vanderBase:Np2D) -> Np1D: """Exact residual-based error estimator.""" self.setupApprox() self.assembleReducedResidualBlocks(full = True) - nAs = self.HFEngine.nAs - 1 - nbs = max(self.HFEngine.nbs - 1, nAs * self.homogeneized) - delta = len(self.mus) - len(self.trainedModel.data.Q) - nbsEff = max(0, nbs - delta) - momentQ = np.zeros(nbsEff, dtype = np.complex) - momentQu = np.zeros((len(self.mus), nAs), dtype = np.complex) - radiusbTen = np.zeros((nbsEff, nbsEff, vanderBase.shape[1]), - dtype = np.complex) - radiusATen = np.zeros((nAs, nAs, vanderBase.shape[1]), - dtype = np.complex) - if nbsEff > 0: - momentQ[0] = self.trainedModel.data.Q[-1] - radiusbTen[0, :, :] = vanderBase[: nbsEff, :] - momentQu[:, 0] = self.trainedModel.data.P[-1, :] - radiusATen[0, :, :] = vanderBase[: nAs, :] + nAsM = self.HFEngine.nAs - 1 + nbsM = max(self.HFEngine.nbs - 1, nAsM * self.homogeneized) + radiusA = np.zeros((len(self.mus), nAsM, vanderBase.shape[1]), + dtype = np.complex) + radiusb = np.zeros((nbsM, vanderBase.shape[1]), dtype = np.complex) + domcoeff = polydomcoeff(self.trainedModel.data.Q.deg[0], + self.trainedModel.data.Q.polybasis) Qvals = self.trainedModel.getQVal(self.mus) - for k in range(1, max(nAs, nbs * (nbsEff > 0))): + for k in range(max(nAsM, nbsM)): + if k < nAsM: + radiusA[:, k :, :] += np.multiply.outer(Qvals * self._fitinv, + vanderBase[: nAsM - k, :]) + if k < nbsM: + radiusb[k :, :] += (self._fitinv.dot(Qvals) + * vanderBase[: nbsM - k, :]) Qvals = Qvals * muRTrain - if k > delta and k < nbs: - momentQ[k - delta] = self._fitinv.dot(Qvals) - radiusbTen[k - delta, k :, :] = ( - radiusbTen[0, : delta - k, :]) - if k < nAs: - momentQu[:, k] = Qvals * self._fitinv - radiusATen[k, k :, :] = radiusATen[0, : - k, :] - if self.POD and nAs > 1: - momentQu[:, 1 :] = self.samplingEngine.RPOD.dot( - momentQu[:, 1 :]) - radiusA = np.tensordot(momentQu, radiusATen, 1) - if nbsEff > 0: - radiusb = np.tensordot(momentQ, radiusbTen, 1) - # 'ij,jk,ik->k', resbb, radiusb, radiusb.conj() - ff = np.sum(self.trainedModel.data.resbb[delta + 1 :, delta + 1 :]\ - .dot(radiusb) * radiusb.conj(), axis = 0) - # 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj() - Lf = np.sum(np.tensordot( - self.trainedModel.data.resAb[delta :, :, :], radiusA, 2) - * radiusb.conj(), axis = 0) - else: - ff, Lf = 0., 0. + if self.POD: + radiusA = np.tensordot(self.samplingEngine.RPOD, radiusA, 1) # 'ijkl,klt,ijt->t', resAA, radiusA, radiusA.conj() LL = np.sum(np.tensordot(self.trainedModel.data.resAA, radiusA, 2) * radiusA.conj(), axis = (0, 1)) - scalingDom = polydomcoeff(len(self.mus) - 1, self.polybasis) - return scalingDom * np.power(np.abs(ff - 2. * np.real(Lf) + LL), .5) + # 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj() + Lf = np.sum(np.tensordot(self.trainedModel.data.resAb[1 :, :, :], + radiusA, 2) * radiusb.conj(), axis = 0) + # 'ij,jk,ik->k', resbb, radiusb, radiusb.conj() + ff = np.sum(self.trainedModel.data.resbb[1 :, 1 :].dot(radiusb) + * radiusb.conj(), axis = 0) + return domcoeff * np.abs(ff - 2. * np.real(Lf) + LL) ** .5 def errorEstimator(self, mus:Np1D) -> Np1D: """Standard residual-based error estimator.""" self.setupApprox() - if (np.any(np.isnan(self.trainedModel.data.P[-1, :])) - or np.any(np.isinf(self.trainedModel.data.P[-1, :]))): - err = np.empty(len(mus)) - err[:] = np.inf - return err - nAs = self.HFEngine.nAs - 1 - nbs = max(self.HFEngine.nbs - 1, nAs * self.homogeneized) + nAsM = self.HFEngine.nAs - 1 + nbsM = max(self.HFEngine.nbs - 1, nAsM * self.homogeneized) muRTest = self.centerNormalize(mus)(0) muRTrain = self.centerNormalize(self.mus)(0) samplingRatio = self._errorSamplingRatio(mus, muRTest, muRTrain) - vanderBase = np.polynomial.polynomial.polyvander(muRTest, - max(nAs, nbs)).T - RHSnorms = self._RHSNorms(vanderBase[: nbs + 1, :]) + if self.errorEstimatorKind == "EXACT": + vanSize = max(nAsM, nbsM) + else: + vanSize = nbsM + vanderBase = np.polynomial.polynomial.polyvander(muRTest, vanSize).T + RHSnorms = self._RHSNorms(vanderBase[: nbsM + 1, :]) if self.errorEstimatorKind == "BARE": jOpt = self._errorEstimatorBare() elif self.errorEstimatorKind == "BASIC": idx_muTestSample = np.argmax(samplingRatio) jOpt = self._errorEstimatorBasic(mus[idx_muTestSample], samplingRatio[idx_muTestSample]) else: #if self.errorEstimatorKind == "EXACT": jOpt = self._errorEstimatorExact(muRTrain, vanderBase[: -1, :]) return jOpt * samplingRatio / RHSnorms def setupApprox(self, plotEst : bool = False): """ - Compute Pade' interpolant. + Compute rational interpolant. SVD-based robust eigenvalue management. """ if self.checkComputedApprox(): return RROMPyAssert(self._mode, message = "Cannot setup approximant.") - if self.verbosity >= 5: - verbosityDepth("INIT", "Setting up {}.". format(self.name()), - timestamp = self.timestamp) + vbMng(self, "INIT", "Setting up {}.".format(self.name()), 5) self.computeScaleFactor() + if not hasattr(self, "As") or not hasattr(self, "bs"): + vbMng(self, "INIT", "Computing affine blocks of system.", 10) + self.As = self.HFEngine.affineLinearSystemA(self.mu0, + self.scaleFactor)[1 :] + self.bs = self.HFEngine.affineLinearSystemb(self.mu0, + self.scaleFactor, + self.homogeneized) + vbMng(self, "DEL", "Done computing affine blocks.", 10) self.greedy(plotEst) self._S = len(self.mus) - self._N, self._M, self._E = [self._S - 1] * 3 - if self.Delta < 0: - self._M += self.Delta - else: - self._N -= self.Delta + self._N, self._M = [self._S - 1] * 2 if self.trainedModel is None: self.trainedModel = tModel() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp data = TrainedModelData(self.trainedModel.name(), self.mu0, self.samplingEngine.samples, + self.scaleFactor, self.HFEngine.rescalingExp) - data.polytype = self.polybasis - data.polytypeP = self.polybasisP - data.scaleFactor = self.scaleFactor data.mus = copy(self.mus) self.trainedModel.data = data else: self.trainedModel = self.trainedModel self.trainedModel.data.projMat = copy(self.samplingEngine.samples) self.trainedModel.data.mus = copy(self.mus) - if min(self.M, self.N) < 0: - if self.verbosity >= 5: - verbosityDepth("MAIN", "Minimal sample size not achieved.", - timestamp = self.timestamp) - Q = np.empty(tuple([max(self.N, 0) + 1] * self.npar), - dtype = np.complex) - P = np.empty(tuple([max(self.M, 0) + 1] * self.npar) - + (len(self.mus),), dtype = np.complex) - Q[:] = np.nan - P[:] = np.nan - self.trainedModel.data.Q = copy(Q) - self.trainedModel.data.P = copy(P) - self.trainedModel.data.approxParameters = copy( - self.approxParameters) - if self.verbosity >= 5: - verbosityDepth("DEL", "Aborting computation of approximant.", - timestamp = self.timestamp) - return + self.catchInstability = True if self.N > 0: - Q = self._setupDenominator() + Qf = self._setupDenominator() + Q = Qf[0] + if self.errorEstimatorKind == "EXACT": self._fitinv = Qf[1] else: - Q = np.ones(tuple([1] * self.npar), dtype = np.complex) + Q = PI() + Q.coeffs = np.ones(1, dtype = np.complex) + Q.npar = 1 + Q.polybasis = self.polybasis + if self.errorEstimatorKind == "EXACT": self._fitinv = np.ones(1) self.trainedModel.data.Q = copy(Q) self.trainedModel.data.P = copy(self._setupNumerator()) self.trainedModel.data.approxParameters = copy(self.approxParameters) - if self.verbosity >= 5: - verbosityDepth("DEL", "Done setting up approximant.", - timestamp = self.timestamp) - - def assembleReducedResidualBlocks(self, full : bool = False): - """Build affine blocks of reduced linear system through projections.""" - scaling = self.trainedModel.data.scaleFactor[0] - self.assembleReducedResidualBlocksbb(self.bs, scaling) - if full: - pMat = self.trainedModel.data.projMat - self.assembleReducedResidualBlocksAb(self.As, self.bs[1 :], - pMat, scaling) - self.assembleReducedResidualBlocksAA(self.As, pMat, scaling) - + vbMng(self, "DEL", "Done setting up approximant.", 5) diff --git a/rrompy/reduction_methods/distributed_greedy/rb_distributed_greedy.py b/rrompy/reduction_methods/greedy/reduced_basis_greedy.py similarity index 72% rename from rrompy/reduction_methods/distributed_greedy/rb_distributed_greedy.py rename to rrompy/reduction_methods/greedy/reduced_basis_greedy.py index d7bab29..55ae357 100644 --- a/rrompy/reduction_methods/distributed_greedy/rb_distributed_greedy.py +++ b/rrompy/reduction_methods/greedy/reduced_basis_greedy.py @@ -1,255 +1,227 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from copy import deepcopy as copy -from .generic_distributed_greedy_approximant import \ - GenericDistributedGreedyApproximant -from rrompy.reduction_methods.distributed import RBDistributed -from rrompy.reduction_methods.trained_model import TrainedModelRB as tModel +from .generic_greedy_approximant import GenericGreedyApproximant +from rrompy.reduction_methods.standard import ReducedBasis +from rrompy.reduction_methods.trained_model import \ + TrainedModelReducedBasis as tModel from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.utilities.base.types import Np1D, DictAny, HFEng, paramVal -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.exception_manager import RROMPyWarning, RROMPyAssert from rrompy.parameter import checkParameterList -__all__ = ['RBDistributedGreedy'] +__all__ = ['ReducedBasisGreedy'] -class RBDistributedGreedy(GenericDistributedGreedyApproximant, RBDistributed): +class ReducedBasisGreedy(GenericGreedyApproximant, ReducedBasis): """ ROM greedy RB approximant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'S': number of starting training points; - 'sampler': sample point generator; - 'greedyTol': uniform error tolerance for greedy algorithm; defaults to 1e-2; - 'interactive': whether to interactively terminate greedy algorithm; defaults to False; - 'maxIter': maximum number of greedy steps; defaults to 1e2; - 'refinementRatio': ratio of test points to be exhausted before test set refinement; defaults to 0.2; - 'nTestPoints': number of test points; defaults to 5e2; - 'trainSetGenerator': training sample points generator; defaults - to Chebyshev sampler within muBounds. + to sampler. Defaults to empty dict. homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults to False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. homogeneized: Whether to homogeneize Dirichlet BCs. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. - 'greedyTol': uniform error tolerance for greedy algorithm; - 'interactive': whether to interactively terminate greedy algorithm; - 'maxIter': maximum number of greedy steps; - 'refinementRatio': ratio of test points to be exhausted before test set refinement; - 'nTestPoints': number of test points; - 'trainSetGenerator': training sample points generator. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator. POD: whether to compute POD of snapshots. S: number of test points. sampler: Sample point generator. greedyTol: uniform error tolerance for greedy algorithm. interactive: whether to interactively terminate greedy algorithm. maxIter: maximum number of greedy steps. refinementRatio: ratio of training points to be exhausted before training set refinement. nTestPoints: number of starting training points. trainSetGenerator: training sample points generator. muBounds: list of bounds for parameter values. samplingEngine: Sampling engine. estimatorNormEngine: Engine for estimator norm computation. uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as sampleList. lastSolvedHF: Parameter(s) corresponding to last computed high fidelity solution(s) as parameterList. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApprox: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. As: List of sparse matrices (in CSC format) representing coefficients - of linear system matrix wrt theta(mu). + of linear system matrix. 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. + RHS. ARBs: List of sparse matrices (in CSC format) representing coefficients - of compressed linear system matrix wrt theta(mu). + of compressed linear system matrix. bRBs: List of numpy vectors representing coefficients of compressed - linear system RHS wrt theta(mu). + linear system RHS. """ def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() + self._addParametersToList([], [], toBeExcluded = ["R", "PODTolerance"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) - if self.verbosity >= 10: - verbosityDepth("INIT", "Computing affine blocks of system.", - timestamp = self.timestamp) - self.As = self.HFEngine.affineLinearSystemA(self.mu0) - self.bs = self.HFEngine.affineLinearSystemb(self.mu0, - self.homogeneized) - if self.verbosity >= 10: - verbosityDepth("DEL", "Done computing affine blocks.", - timestamp = self.timestamp) self._postInit() @property def R(self): """Value of R.""" self._R = np.prod(self._S) return self._R @R.setter def R(self, R): RROMPyWarning(("R is used just to simplify inheritance, and its value " "cannot be changed from that of prod(S).")) @property def PODTolerance(self): """Value of PODTolerance.""" self._PODTolerance = -1 return self._PODTolerance @PODTolerance.setter def PODTolerance(self, PODTolerance): RROMPyWarning(("PODTolerance is used just to simplify inheritance, " "and its value cannot be changed from -1.")) def errorEstimator(self, mus:Np1D) -> Np1D: """ Standard residual-based error estimator. Unreliable for unstable problems (inf-sup constant is missing). """ self.setupApprox() self.assembleReducedResidualBlocks(full = True) nmus = len(mus) nAs = self.trainedModel.data.resAA.shape[1] nbs = self.trainedModel.data.resbb.shape[0] - thetaAs = self.trainedModel.data.thetaAs - thetabs = self.trainedModel.data.thetabs radiusA = np.empty((len(self.mus), nAs, nmus), dtype = np.complex) radiusb = np.empty((nbs, nmus), dtype = np.complex) + mustr = mus + if nmus > 2: + mustr = "[{} ..({}).. {}]".format(mus[0], nmus - 2, mus[-1]) + vbMng(self.trainedModel, "INIT", + "Computing RB solution at mu = {}.".format(mustr), 5) verb = self.trainedModel.verbosity self.trainedModel.verbosity = 0 - if verb >= 5: - mustr = mus - if nmus > 2: - mustr = "[{} ..({}).. {}]".format(mus[0], nmus - 2, mus[-1]) - verbosityDepth("INIT", ("Computing RB solution at mu = " - "{}.").format(mustr), - timestamp = self.timestamp) - parmus, _ = checkParameterList(mus, self.npar) + parmus = checkParameterList(mus, self.npar)[0] uApproxRs = self.getApproxReduced(parmus) + mu0Eff = self.mu0(0, 0) ** self.HFEngine.rescalingExp[0] for j, muPL in enumerate(parmus): - mu = muPL[0] uApproxR = uApproxRs[j] - for i in range(nAs): - radiusA[:, i, j] = eval(thetaAs[i]) * uApproxR - for i in range(nbs): - radiusb[i, j] = eval(thetabs[i]) - if verb >= 5: - verbosityDepth("DEL", "Done computing RB solution.", - timestamp = self.timestamp) + for i in range(max(nAs, nbs)): + thetai = ((muPL[0] ** self.HFEngine.rescalingExp[0] + - mu0Eff) / self.scaleFactor[0]) ** i + if i < nAs: + radiusA[:, i, j] = thetai * uApproxR + if i < nbs: + radiusb[i, j] = thetai self.trainedModel.verbosity = verb + vbMng(self.trainedModel, "DEL", "Done computing RB solution.", 5) # 'ij,jk,ik->k', resbb, radiusb, radiusb.conj() ff = np.sum(self.trainedModel.data.resbb.dot(radiusb) * radiusb.conj(), axis = 0) # 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj() Lf = np.sum(np.tensordot(self.trainedModel.data.resAb, radiusA, 2) * radiusb.conj(), axis = 0) # 'ijkl,klt,ijt->t', resAA, radiusA, radiusA.conj() LL = np.sum(np.tensordot(self.trainedModel.data.resAA, radiusA, 2) * radiusA.conj(), axis = (0, 1)) return np.abs((LL - 2. * np.real(Lf) + ff) / ff) ** .5 def setupApprox(self, plotEst : bool = False): """Compute RB projection matrix.""" if self.checkComputedApprox(): return RROMPyAssert(self._mode, message = "Cannot setup approximant.") - if self.verbosity >= 5: - verbosityDepth("INIT", "Setting up {}.". format(self.name()), - timestamp = self.timestamp) + vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5) + self.computeScaleFactor() self.greedy(plotEst) - if self.verbosity >= 7: - verbosityDepth("INIT", "Computing projection matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Computing projection matrix.", 7) pMat = self.samplingEngine.samples - + vbMng(self, "INIT", "Computing affine blocks of system.", 10) + self.As = self.HFEngine.affineLinearSystemA(self.mu0, self.scaleFactor) + self.bs = self.HFEngine.affineLinearSystemb(self.mu0, self.scaleFactor, + self.homogeneized) + vbMng(self, "DEL", "Done computing affine blocks.", 10) if self.trainedModel is None: self.trainedModel = tModel() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp data = TrainedModelData(self.trainedModel.name(), self.mu0, - pMat, self.HFEngine.rescalingExp) - data.thetaAs = self.HFEngine.affineWeightsA(self.mu0) - data.thetabs = self.HFEngine.affineWeightsb(self.mu0, - self.homogeneized) + pMat, self.scaleFactor, + self.HFEngine.rescalingExp) ARBs, bRBs = self.assembleReducedSystem(pMat) self.trainedModel.data = data else: self.trainedModel = self.trainedModel pMatOld = self.trainedModel.data.projMat Sold = pMatOld.shape[1] idxNew = list(range(Sold, pMat.shape[1])) ARBs, bRBs = self.assembleReducedSystem(pMat(idxNew), pMatOld) self.trainedModel.data.projMat = copy(pMat) self.trainedModel.data.mus = copy(self.mus) self.trainedModel.data.ARBs = ARBs self.trainedModel.data.bRBs = bRBs - if self.verbosity >= 7: - verbosityDepth("DEL", "Done computing projection matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done computing projection matrix.", 7) self.trainedModel.data.approxParameters = copy(self.approxParameters) - if self.verbosity >= 5: - verbosityDepth("DEL", "Done setting up approximant.", - timestamp = self.timestamp) - - def assembleReducedResidualBlocks(self, full : bool = False): - """Build affine blocks of RB linear system through projections.""" - self.assembleReducedResidualBlocksbb(self.bs) - if full: - pMat = self.trainedModel.data.projMat - self.assembleReducedResidualBlocksAb(self.As, self.bs, pMat) - self.assembleReducedResidualBlocksAA(self.As, pMat) + vbMng(self, "DEL", "Done setting up approximant.", 5) diff --git a/rrompy/reduction_methods/distributed/__init__.py b/rrompy/reduction_methods/pivoted/__init__.py similarity index 71% copy from rrompy/reduction_methods/distributed/__init__.py copy to rrompy/reduction_methods/pivoted/__init__.py index 1d7c1fc..6237733 100644 --- a/rrompy/reduction_methods/distributed/__init__.py +++ b/rrompy/reduction_methods/pivoted/__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_distributed_approximant import GenericDistributedApproximant -from .rational_interpolant import RationalInterpolant -from .rb_distributed import RBDistributed +from .generic_pivoted_approximant import GenericPivotedApproximant +from .rational_interpolant_pivoted import RationalInterpolantPivoted +from .reduced_basis_pivoted import ReducedBasisPivoted __all__ = [ - 'GenericDistributedApproximant', - 'RationalInterpolant', - 'RBDistributed' + 'GenericPivotedApproximant', + 'RationalInterpolantPivoted', + 'ReducedBasisPivoted' ] diff --git a/rrompy/reduction_methods/pivoted/generic_pivoted_approximant.py b/rrompy/reduction_methods/pivoted/generic_pivoted_approximant.py new file mode 100644 index 0000000..57576d0 --- /dev/null +++ b/rrompy/reduction_methods/pivoted/generic_pivoted_approximant.py @@ -0,0 +1,480 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from copy import deepcopy as copy +from rrompy.reduction_methods.base.generic_approximant import ( + GenericApproximant) +from rrompy.utilities.poly_fitting.polynomial import (polybases, + nextDerivativeIndices, + PolynomialInterpolator as PI) +from rrompy.utilities.poly_fitting.radial_basis import ( + RadialBasisInterpolator as RBI) +from rrompy.utilities.poly_fitting.radial_basis import rbbases +from rrompy.sampling.pivoted import (SamplingEnginePivoted, + SamplingEnginePivotedPOD) +from rrompy.utilities.base.types import (ListAny, DictAny, HFEng, paramVal, + paramList) +from rrompy.utilities.base import verbosityManager as vbMng +from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, + RROMPyWarning) +from rrompy.parameter import emptyParameterList + +__all__ = ['GenericPivotedApproximant'] + +class GenericPivotedApproximant(GenericApproximant): + """ + ROM pivoted approximant computation for parametric problems (ABSTRACT). + + Args: + HFEngine: HF problem solver. + mu0(optional): Default parameter. Defaults to 0. + directionPivot(optional): Pivot components. Defaults to [0]. + approxParameters(optional): Dictionary containing values for main + parameters of approximant. Recognized keys are: + - 'POD': whether to compute POD of snapshots; defaults to True; + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' + and 'LEGENDRE'; defaults to 'MONOMIAL'; + - 'MMarginal': degree of marginal interpolant; defaults to 0; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; defaults to 0, i.e. no radial basis; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; defaults to 0, i.e. identity; + - 'interpRcondMarginal': tolerance for marginal interpolation; + defaults to None. + Defaults to empty dict. + homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults + to False. + verbosity(optional): Verbosity level. Defaults to 10. + + Attributes: + HFEngine: HF problem solver. + mu0: Default parameter. + directionPivot: Pivot components. + mus: Array of snapshot parameters. + musPivot: Array of pivot snapshot parameters. + musMarginal: Array of marginal snapshot parameters. + homogeneized: Whether to homogeneize Dirichlet BCs. + approxParameters: Dictionary containing values for main parameters of + approximant. Recognized keys are in parameterList. + parameterListSoft: Recognized keys of soft approximant parameters: + - 'POD': whether to compute POD of snapshots; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; + - 'MMarginal': degree of marginal interpolant; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; + - 'interpRcondMarginal': tolerance for marginal interpolation. + parameterListCritical: Recognized keys of critical approximant + parameters: + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator. + POD: Whether to compute POD of snapshots. + S: Total number of pivot samples current approximant relies upon. + samplerPivot: Pivot sample point generator. + SMarginal: Total number of marginal samples current approximant relies + upon. + samplerMarginal: Marginal sample point generator. + polybasisMarginal: Type of polynomial basis for marginal interpolation. + MMarginal: Degree of marginal interpolant. + radialBasisMarginal: Radial basis family for marginal interpolant. + radialBasisWeightsMarginal: Radial basis weights for marginal + interpolant. + interpRcondMarginal: Tolerance for marginal interpolation. + muBoundsPivot: list of bounds for pivot parameter values. + muBoundsMarginal: list of bounds for marginal parameter values. + samplingEngine: Sampling engine. + uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as + sampleList. + lastSolvedHF: Parameter(s) corresponding to last computed high fidelity + solution(s) as parameterList. + uApproxReduced: Reduced approximate solution(s) with parameter(s) + lastSolvedApprox as sampleList. + lastSolvedApproxReduced: Parameter(s) corresponding to last computed + reduced approximate solution(s) as parameterList. + uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as + sampleList. + lastSolvedApprox: Parameter(s) corresponding to last computed + approximate solution(s) as parameterList. + """ + + def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, + directionPivot : ListAny = [0], + approxParameters : DictAny = {}, homogeneized : bool = False, + verbosity : int = 10, timestamp : bool = True): + self._preInit() + from rrompy.parameter.parameter_sampling import QuadratureSampler as QS + QSBase = QS([[0], [1]], "UNIFORM") + self._addParametersToList( + ["polybasisMarginal", "MMarginal", "radialBasisMarginal", + "radialBasisWeightsMarginal", "interpRcondMarginal"], + ["MONOMIAL", 0, 0, 1, -1], + ["samplerPivot", "SMarginal", "samplerMarginal"], + [QSBase, [1], QSBase]) + del QS + self._directionPivot = directionPivot + super().__init__(HFEngine = HFEngine, mu0 = mu0, + approxParameters = approxParameters, + homogeneized = homogeneized, + verbosity = verbosity, timestamp = timestamp) + self._postInit() + + def setupSampling(self): + """Setup sampling engine.""" + RROMPyAssert(self._mode, message = "Cannot setup sampling engine.") + if not hasattr(self, "_POD") or self._POD is None: return + if self.POD: + SamplingEngine = SamplingEnginePivotedPOD + else: + SamplingEngine = SamplingEnginePivoted + self.samplingEngine = SamplingEngine(self.HFEngine, + self.directionPivot, + verbosity = self.verbosity, + allowRepeatedSamples = True) + + @property + def SMarginal(self): + """Value of SMarginal.""" + return self._SMarginal + @SMarginal.setter + def SMarginal(self, SMarginal): + if not hasattr(SMarginal, "__len__"): SMarginal = [SMarginal] + if any([s <= 0 for s in SMarginal]): + raise RROMPyException("SMarginal must be positive.") + if hasattr(self, "_SMarginal") and self._SMarginal is not None: + Sold = tuple(self.SMarginal) + else: Sold = -1 + self._SMarginal = SMarginal + self._approxParameters["SMarginal"] = self.SMarginal + if Sold != tuple(self.SMarginal): + self.resetSamples() + + @property + def polybasisMarginal(self): + """Value of polybasisMarginal.""" + return self._polybasisMarginal + @polybasisMarginal.setter + def polybasisMarginal(self, polybasisMarginal): + try: + polybasisMarginal = polybasisMarginal.upper().strip().replace(" ", + "") + if polybasisMarginal not in polybases: + raise RROMPyException( + "Prescribed marginal polybasis not recognized.") + self._polybasisMarginal = polybasisMarginal + except: + RROMPyWarning(("Prescribed marginal polybasis not recognized. " + "Overriding to 'MONOMIAL'.")) + self._polybasisMarginal = "MONOMIAL" + self._approxParameters["polybasisMarginal"] = self.polybasisMarginal + + @property + def MMarginal(self): + """Value of MMarginal.""" + return self._MMarginal + @MMarginal.setter + def MMarginal(self, MMarginal): + if MMarginal < 0: + raise RROMPyException("MMarginal must be non-negative.") + self._MMarginal = MMarginal + self._approxParameters["MMarginal"] = self.MMarginal + + @property + def radialBasisMarginal(self): + """Value of radialBasisMarginal.""" + return self._radialBasisMarginal + @radialBasisMarginal.setter + def radialBasisMarginal(self, radialBasisMarginal): + try: + if radialBasisMarginal != 0: + radialBasisMarginal = radialBasisMarginal.upper().strip()\ + .replace(" ","") + if radialBasisMarginal not in rbbases: + raise RROMPyException(("Prescribed marginal radialBasis " + "not recognized.")) + self._radialBasisMarginal = radialBasisMarginal + except: + RROMPyWarning(("Prescribed marginal radialBasis not recognized. " + "Overriding to 0.")) + self._radialBasisMarginal = 0 + self._approxParameters["radialBasisMarginal"] = ( + self.radialBasisMarginal) + + @property + def radialBasisWeightsMarginal(self): + """Value of radialBasisWeightsMarginal.""" + return self._radialBasisWeightsMarginal + @radialBasisWeightsMarginal.setter + def radialBasisWeightsMarginal(self, radialBasisWeightsMarginal): + self._radialBasisWeightsMarginal = radialBasisWeightsMarginal + self._approxParameters["radialBasisWeightsMarginal"] = ( + self.radialBasisWeightsMarginal) + + @property + def polybasisMarginalP(self): + if self.radialBasisMarginal == 0: + return self._polybasisMarginal + return self._polybasisMarginal + "_" + self.radialBasisMarginal + + @property + def interpRcondMarginal(self): + """Value of interpRcondMarginal.""" + return self._interpRcondMarginal + @interpRcondMarginal.setter + def interpRcondMarginal(self, interpRcondMarginal): + self._interpRcondMarginal = interpRcondMarginal + self._approxParameters["interpRcondMarginal"] = ( + self.interpRcondMarginal) + + @property + def directionPivot(self): + """Value of directionPivot. Its assignment may reset snapshots.""" + return self._directionPivot + @directionPivot.setter + def directionPivot(self, directionPivot): + if hasattr(self, '_directionPivot'): + directionPivotOld = copy(self.directionPivot) + else: + directionPivotOld = None + if (directionPivotOld is None + or len(directionPivot) != len(directionPivotOld) + or not directionPivot == directionPivotOld): + self.resetSamples() + self._directionPivot = directionPivot + + @property + def directionMarginal(self): + return [x for x in range(self.HFEngine.npar) \ + if x not in self.directionPivot] + + @property + def rescalingExpPivot(self): + return [self.HFEngine.rescalingExp[x] for x in self.directionPivot] + + @property + def rescalingExpMarginal(self): + return [self.HFEngine.rescalingExp[x] for x in self.directionMarginal] + + @property + def muBoundsPivot(self): + """Value of muBoundsPivot.""" + return self.samplerPivot.lims + + @property + def muBoundsMarginal(self): + """Value of muBoundsMarginal.""" + return self.samplerMarginal.lims + + @property + def samplerPivot(self): + """Value of samplerPivot.""" + return self._samplerPivot + @samplerPivot.setter + def samplerPivot(self, samplerPivot): + if 'generatePoints' not in dir(samplerPivot): + raise RROMPyException("Pivot sampler type not recognized.") + if hasattr(self, '_samplerPivot') and self._samplerPivot is not None: + samplerOld = self.samplerPivot + self._samplerPivot = samplerPivot + self._approxParameters["samplerPivot"] = self.samplerPivot.__str__() + if not 'samplerOld' in locals() or samplerOld != self.samplerPivot: + self.resetSamples() + + @property + def samplerMarginal(self): + """Value of samplerMarginal.""" + return self._samplerMarginal + @samplerMarginal.setter + def samplerMarginal(self, samplerMarginal): + if 'generatePoints' not in dir(samplerMarginal): + raise RROMPyException("Marginal sampler type not recognized.") + if (hasattr(self, '_samplerMarginal') + and self._samplerMarginal is not None): + samplerOld = self.samplerMarginal + self._samplerMarginal = samplerMarginal + self._approxParameters["samplerMarginal"] = ( + self.samplerMarginal.__str__()) + if not 'samplerOld' in locals() or samplerOld != self.samplerMarginal: + self.resetSamples() + + def resetSamples(self): + """Reset samples.""" + super().resetSamples() + self._musMUniqueCN = None + self._derMIdxs = None + self._reorderM = None + + def setSamples(self, samplingEngine): + """Copy samplingEngine and samples.""" + super().setSamples(samplingEngine) + self.mus = copy(self.samplingEngine[0].mus) + for sEj in self.samplingEngine[1:]: + self.mus.append(sEj.mus) + + def computeSnapshots(self): + """Compute snapshots of solution map.""" + RROMPyAssert(self._mode, + message = "Cannot start snapshot computation.") + Sp = np.prod(self.S) + Seff = Sp * np.prod(self.SMarginal) + if self.samplingEngine.nsamplesTot != Seff: + self.resetSamples() + vbMng(self, "INIT", "Starting computation of snapshots.", 5) + if self.HFEngine.As[0] is None: self.HFEngine.A(self.mu0) + if self.HFEngine.bs[0] is None: self.HFEngine.b(self.mu0) + self.musPivot = self.samplerPivot.generatePoints(self.S) + self.musMarginal = self.samplerMarginal.generatePoints( + self.SMarginal) + self.mus = emptyParameterList() + self.mus.reset((Seff, self.HFEngine.npar)) + self.samplingEngine.resetHistory(Seff // Sp) + for j, muMarg in enumerate(self.musMarginal): + for k in range(j * Sp, (j + 1) * Sp): + self.mus.data[k, self.directionPivot] = ( + self.musPivot[k - j * Sp].data) + self.mus.data[k, self.directionMarginal] = muMarg.data + self.samplingEngine.iterSample(self.musPivot, self.musMarginal, + homogeneized = self.homogeneized) + if self.POD: + self.samplingEngine.coalesceSamples(self.interpRcondMarginal) + else: + self.samplingEngine.coalesceSamples() + vbMng(self, "DEL", "Done computing snapshots.", 5) + + def _setupMarginalInterpolationIndices(self): + """Setup parameters for polyvander.""" + RROMPyAssert(self._mode, + message = "Cannot setup interpolation indices.") + if (self._musMUniqueCN is None + or len(self._reorderM) != len(self.musMarginal)): + self._musMUniqueCN, musMIdxsTo, musMIdxs, musMCount = ( + self.centerNormalizeMarginal(self.musMarginal).unique( + return_index = True, + return_inverse = True, + return_counts = True)) + self._musMUnique = self.musMarginal[musMIdxsTo] + self._derMIdxs = [None] * len(self._musMUniqueCN) + self._reorderM = np.empty(len(musMIdxs), dtype = int) + filled = 0 + for j, cnt in enumerate(musMCount): + self._derMIdxs[j] = nextDerivativeIndices([], + self.musMarginal.shape[1], cnt) + jIdx = np.nonzero(musMIdxs == j)[0] + self._reorderM[jIdx] = np.arange(filled, filled + cnt) + filled += cnt + + def _setupMarginalInterp(self): + """Compute marginal interpolator.""" + RROMPyAssert(self._mode, message = "Cannot setup numerator.") + vbMng(self, "INIT", "Starting computation of marginal interpolator.", + 7) + self._setupMarginalInterpolationIndices() + MMarginal0 = copy(self.MMarginal) + mI = [] + for j in range(len(self.musMarginal)): + canonicalj = 1. * (np.arange(len(self.musMarginal)) == j) + self._MMarginal = MMarginal0 + while self.MMarginal >= 0: + if self.radialBasisMarginal == 0: + p = PI() + wellCond, msg = p.setupByInterpolation( + self._musMUniqueCN, canonicalj, self.MMarginal, + self.polybasisMarginal, self.verbosity >= 5, True, + {"derIdxs": self._derMIdxs, + "reorder": self._reorderM, + "scl": np.power(self.scaleFactorMarginal, -1.)}, + {"rcond": self.interpRcondMarginal}) + else: + p = RBI() + wellCond, msg = p.setupByInterpolation( + self._musMUniqueCN, canonicalj, self.MMarginal, + self.polybasisMarginalP, + self.radialBasisWeightsMarginal, + self.verbosity >= 5, True, + {"derIdxs": self._derMIdxs, + "reorder": self._reorderM, + "scl": np.power(self.scaleFactorMarginal, -1.)}, + {"rcond": self.interpRcondMarginal}) + vbMng(self, "MAIN", msg, 5) + if wellCond: break + RROMPyWarning(("Polyfit is poorly conditioned. Reducing " + "MMarginal by 1.")) + self.MMarginal -= 1 + if self.MMarginal < 0: + raise RROMPyException(("Instability in computation of " + "marginal interpolator. Aborting.")) + mI = mI + [copy(p)] + vbMng(self, "DEL", "Done computing marginal interpolator.", 7) + return mI + + def normApprox(self, mu:paramList, homogeneized : bool = False) -> float: + """ + Compute norm of approximant at arbitrary parameter. + + Args: + mu: Target parameter. + homogeneized(optional): Whether to remove Dirichlet BC. Defaults to + False. + + Returns: + Target norm of approximant. + """ + if not self.POD or self.homogeneized != homogeneized: + return super().normApprox(mu, homogeneized) + return np.linalg.norm(self.getApproxReduced(mu).data, axis = 0) + + def computeScaleFactor(self): + """Compute parameter rescaling factor.""" + RROMPyAssert(self._mode, message = "Cannot compute rescaling factor.") + self.scaleFactorPivot = .5 * np.abs( + self.muBoundsPivot[0] ** self.rescalingExpPivot + - self.muBoundsPivot[1] ** self.rescalingExpPivot) + self.scaleFactorMarginal = .5 * np.abs( + self.muBoundsMarginal[0] ** self.rescalingExpMarginal + - self.muBoundsMarginal[1] ** self.rescalingExpMarginal) + self.scaleFactor = np.empty(self.npar) + self.scaleFactor[self.directionPivot] = self.scaleFactorPivot + self.scaleFactor[self.directionMarginal] = self.scaleFactorMarginal + + def centerNormalizeMarginal(self, mu : paramList = [], + mu0 : paramVal = None) -> paramList: + """ + Compute normalized parameter to be plugged into approximant. + + Args: + mu: Parameter(s) 1. + mu0: Parameter(s) 2. If None, set to self.mu0. + + Returns: + Normalized parameter. + """ + return self.trainedModel.centerNormalizeMarginal(mu, mu0) + diff --git a/rrompy/reduction_methods/pivoted/rational_interpolant_pivoted.py b/rrompy/reduction_methods/pivoted/rational_interpolant_pivoted.py new file mode 100644 index 0000000..33ec944 --- /dev/null +++ b/rrompy/reduction_methods/pivoted/rational_interpolant_pivoted.py @@ -0,0 +1,614 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +from copy import deepcopy as copy +import numpy as np +from rrompy.reduction_methods.base import checkRobustTolerance +from .generic_pivoted_approximant import GenericPivotedApproximant +from rrompy.reduction_methods.standard.rational_interpolant import ( + RationalInterpolant as RI) +from rrompy.utilities.poly_fitting.polynomial import (polybases, polyfitname, + nextDerivativeIndices, + hashDerivativeToIdx as hashD, + hashIdxToDerivative as hashI, + homogeneizedpolyvander as hpvP, + homogeneizedToFull, + PolynomialInterpolator as PI) +from rrompy.utilities.poly_fitting.radial_basis import (rbbases, + RadialBasisInterpolator as RBI) +from rrompy.reduction_methods.trained_model import (TrainedModelPivotedData, + TrainedModelPivotedRational as tModel) +from rrompy.utilities.base.types import (Np1D, Np2D, HFEng, DictAny, Tuple, + List, ListAny, paramVal, paramList) +from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp +from rrompy.utilities.numerical import multifactorial, customPInv +from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, + RROMPyWarning) +from rrompy.parameter import checkParameter + +__all__ = ['RationalInterpolantPivoted'] + +class RationalInterpolantPivoted(GenericPivotedApproximant): + """ + ROM pivoted rational interpolant computation for parametric problems. + + Args: + HFEngine: HF problem solver. + mu0(optional): Default parameter. Defaults to 0. + directionPivot(optional): Pivot components. Defaults to [0]. + approxParameters(optional): Dictionary containing values for main + parameters of approximant. Recognized keys are: + - 'POD': whether to compute POD of snapshots; defaults to True; + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator; + - 'polybasisPivot': type of polynomial basis for pivot + interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' + and 'LEGENDRE'; defaults to 'MONOMIAL'; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' + and 'LEGENDRE'; defaults to 'MONOMIAL'; + - 'E': number of derivatives used to compute interpolant; defaults + to 0; + - 'M': degree of rational interpolant numerator; defaults to 0; + - 'N': degree of rational interpolant denominator; defaults to 0; + - 'MMarginal': degree of marginal interpolant; defaults to 0; + - 'radialBasisPivot': radial basis family for pivot numerator; + defaults to 0, i.e. no radial basis; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; defaults to 0, i.e. no radial basis; + - 'radialBasisWeightsPivot': radial basis weights for pivot + numerator; defaults to 0, i.e. identity; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; defaults to 0, i.e. identity; + - 'interpRcondPivot': tolerance for pivot interpolation; defaults + to None; + - 'interpRcondMarginal': tolerance for marginal interpolation; + defaults to None; + - 'robustTol': tolerance for robust rational denominator + management; defaults to 0. + Defaults to empty dict. + 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. + directionPivot: Pivot components. + mus: Array of snapshot parameters. + musPivot: Array of pivot snapshot parameters. + musMarginal: Array of marginal snapshot parameters. + homogeneized: Whether to homogeneize Dirichlet BCs. + approxParameters: Dictionary containing values for main parameters of + approximant. Recognized keys are in parameterList. + parameterListSoft: Recognized keys of soft approximant parameters: + - 'POD': whether to compute POD of snapshots; + - 'polybasisPivot': type of polynomial basis for pivot + interpolation; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; + - 'E': number of derivatives used to compute interpolant; + - 'M': degree of rational interpolant numerator; + - 'N': degree of rational interpolant denominator; + - 'MMarginal': degree of marginal interpolant; + - 'radialBasisPivot': radial basis family for pivot numerator; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; + - 'radialBasisWeightsPivot': radial basis weights for pivot + numerator; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; + - 'interpRcondPivot': tolerance for pivot interpolation; + - 'interpRcondMarginal': tolerance for marginal interpolation; + - 'robustTol': tolerance for robust rational denominator + management. + parameterListCritical: Recognized keys of critical approximant + parameters: + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator. + POD: Whether to compute POD of snapshots. + S: Total number of pivot samples current approximant relies upon. + sampler: Pivot sample point generator. + polybasisPivot: Type of polynomial basis for pivot interpolation. + polybasisMarginal: Type of polynomial basis for marginal interpolation. + M: Numerator degree of approximant. + N: Denominator degree of approximant. + MMarginal: Degree of marginal interpolant. + radialBasisPivot: Radial basis family for pivot numerator. + radialBasisMarginal: Radial basis family for marginal interpolant. + radialBasisWeightsPivot: Radial basis weights for pivot numerator. + radialBasisWeightsMarginal: Radial basis weights for marginal + interpolant. + interpRcondPivot: Tolerance for pivot interpolation. + interpRcondMarginal: Tolerance for marginal interpolation. + robustTol: Tolerance for robust rational denominator management. + E: Complete derivative depth level of S. + muBoundsPivot: list of bounds for pivot parameter values. + muBoundsMarginal: list of bounds for marginal parameter values. + samplingEngine: Sampling engine. + uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as + sampleList. + lastSolvedHF: Parameter(s) corresponding to last computed high fidelity + solution(s) as parameterList. + uApproxReduced: Reduced approximate solution(s) with parameter(s) + lastSolvedApprox as sampleList. + lastSolvedApproxReduced: Parameter(s) corresponding to last computed + reduced approximate solution(s) as parameterList. + uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as + sampleList. + lastSolvedApprox: Parameter(s) corresponding to last computed + approximate solution(s) as parameterList. + Q: Numpy 1D vector containing complex coefficients of approximant + denominator. + P: Numpy 2D vector whose columns are FE dofs of coefficients of + approximant numerator. + """ + + def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, + directionPivot : ListAny = [0], + approxParameters : DictAny = {}, homogeneized : bool = False, + verbosity : int = 10, timestamp : bool = True): + self._preInit() + self._addParametersToList( + ["polybasisPivot", "E", "M", "N", + "radialBasisPivot", "radialBasisWeightsPivot", + "interpRcondPivot", "robustTol"], + ["MONOMIAL", -1, 0, 0, 0, 1, -1, 0]) + super().__init__(HFEngine = HFEngine, mu0 = mu0, + directionPivot = directionPivot, + approxParameters = approxParameters, + homogeneized = homogeneized, + verbosity = verbosity, timestamp = timestamp) + self._postInit() + + @property + def polybasisPivot(self): + """Value of polybasisPivot.""" + return self._polybasisPivot + @polybasisPivot.setter + def polybasisPivot(self, polybasisPivot): + try: + polybasisPivot = polybasisPivot.upper().strip().replace(" ","") + if polybasisPivot not in polybases: + raise RROMPyException( + "Prescribed pivot polybasis not recognized.") + self._polybasisPivot = polybasisPivot + except: + RROMPyWarning(("Prescribed pivot polybasis not recognized. " + "Overriding to 'MONOMIAL'.")) + self._polybasisPivot = "MONOMIAL" + self._approxParameters["polybasisPivot"] = self.polybasisPivot + + @property + def radialBasisPivot(self): + """Value of radialBasisPivot.""" + return self._radialBasisPivot + @radialBasisPivot.setter + def radialBasisPivot(self, radialBasisPivot): + try: + if radialBasisPivot != 0: + radialBasisPivot = radialBasisPivot.upper().strip().replace( + " ","") + if radialBasisPivot not in rbbases: + raise RROMPyException(("Prescribed pivot radialBasis not " + "recognized.")) + self._radialBasisPivot = radialBasisPivot + except: + RROMPyWarning(("Prescribed pivot radialBasis not recognized. " + "Overriding to 0.")) + self._radialBasisPivot = 0 + self._approxParameters["radialBasisPivot"] = self.radialBasisPivot + + @property + def radialBasisWeightsPivot(self): + """Value of radialBasisWeightsPivot.""" + return self._radialBasisWeightsPivot + @radialBasisWeightsPivot.setter + def radialBasisWeightsPivot(self, radialBasisWeightsPivot): + self._radialBasisWeightsPivot = radialBasisWeightsPivot + self._approxParameters["radialBasisWeightsPivot"] = ( + self.radialBasisWeightsPivot) + + @property + def polybasisPivotP(self): + if self.radialBasisPivot == 0: + return self._polybasisPivot + return self._polybasisPivot + "_" + self.radialBasisPivot + + @property + def interpRcondPivot(self): + """Value of interpRcondPivot.""" + return self._interpRcondPivot + @interpRcondPivot.setter + def interpRcondPivot(self, interpRcondPivot): + self._interpRcondPivot = interpRcondPivot + self._approxParameters["interpRcondPivot"] = self.interpRcondPivot + + @property + def M(self): + """Value of M. Its assignment may change S.""" + return self._M + @M.setter + def M(self, M): + if M < 0: raise RROMPyException("M must be non-negative.") + self._M = M + self._approxParameters["M"] = self.M + if hasattr(self, "_E") and self.E >= 0 and self.E < self.M: + RROMPyWarning("Prescribed S is too small. Decreasing M.") + self.M = self.E + + @property + def N(self): + """Value of N. Its assignment may change S.""" + return self._N + @N.setter + def N(self, N): + if N < 0: raise RROMPyException("N must be non-negative.") + self._N = N + self._approxParameters["N"] = self.N + if hasattr(self, "_E") and self.E >= 0 and self.E < self.N: + RROMPyWarning("Prescribed S is too small. Decreasing N.") + self.N = self.E + + @property + def E(self): + """Value of E.""" + return self._E + @E.setter + def E(self, E): + if E < 0: + if not hasattr(self, "_S"): + raise RROMPyException(("Value of E must be positive if S is " + "not yet assigned.")) + E = np.sum(hashI(np.prod(self.S), len(self.directionPivot))) - 1 + self._E = E + self._approxParameters["E"] = self.E + if (hasattr(self, "_S") + and self.E >= np.sum(hashI(np.prod(self.S),len(self.directionPivot)))): + RROMPyWarning("Prescribed S is too small. Decreasing E.") + self.E = -1 + if hasattr(self, "_M"): self.M = self.M + if hasattr(self, "_N"): self.N = self.N + + @property + def robustTol(self): + """Value of tolerance for robust rational denominator management.""" + return self._robustTol + @robustTol.setter + def robustTol(self, robustTol): + if robustTol < 0.: + RROMPyWarning(("Overriding prescribed negative robustness " + "tolerance to 0.")) + robustTol = 0. + self._robustTol = robustTol + self._approxParameters["robustTol"] = self.robustTol + + @property + def S(self): + """Value of S.""" + return self._S + @S.setter + def S(self, S): + GenericPivotedApproximant.S.fset(self, S) + if hasattr(self, "_M"): self.M = self.M + if hasattr(self, "_N"): self.N = self.N + if hasattr(self, "_E"): self.E = self.E + + def resetSamples(self): + """Reset samples.""" + super().resetSamples() + self._musPUniqueCN = None + self._derPIdxs = None + self._reorderP = None + + def _setupPivotInterpolationIndices(self): + """Setup parameters for polyvander.""" + RROMPyAssert(self._mode, + message = "Cannot setup interpolation indices.") + if (self._musPUniqueCN is None + or len(self._reorderP) != len(self.musPivot)): + self._musPUniqueCN, musPIdxsTo, musPIdxs, musPCount = ( + self.centerNormalizePivot(self.musPivot).unique( + return_index = True, + return_inverse = True, + return_counts = True)) + self._musPUnique = self.mus[musPIdxsTo] + self._derPIdxs = [None] * len(self._musPUniqueCN) + self._reorderP = np.empty(len(musPIdxs), dtype = int) + filled = 0 + for j, cnt in enumerate(musPCount): + self._derPIdxs[j] = nextDerivativeIndices([], + self.musPivot.shape[1], cnt) + jIdx = np.nonzero(musPIdxs == j)[0] + self._reorderP[jIdx] = np.arange(filled, filled + cnt) + filled += cnt + + def _setupDenominator(self): + """Compute rational denominator.""" + RROMPyAssert(self._mode, message = "Cannot setup denominator.") + vbMng(self, "INIT", "Starting computation of denominator.", 7) + NinvD = None + N0 = copy(self.N) + qs = [] + self.verbosity -= 10 + for j in range(len(self.musMarginal)): + self._N = N0 + while self.N > 0: + if NinvD != self.N: + invD, fitinvP = self._computeInterpolantInverseBlocks() + NinvD = self.N + if self.POD: + ev, eV = RI.findeveVGQR(self, self.samplingEngine.RPOD[j], + invD) + else: + ev, eV = RI.findeveVGExplicit(self, + self.samplingEngine.samples[j], invD) + nevBad = checkRobustTolerance(ev, self.robustTol) + if nevBad <= 1: break + if self.catchInstability: + raise RROMPyException(("Instability in denominator " + "computation: eigenproblem is " + "poorly conditioned.")) + RROMPyWarning(("Smallest {} eigenvalues below tolerance. " + "Reducing N by 1.").format(nevBad)) + self.N = self.N - 1 + if self.N <= 0: + self._N = 0 + eV = np.ones((1, 1)) + q = PI() + q.npar = self.musPivot.shape[1] + q.polybasis = self.polybasisPivot + q.coeffs = homogeneizedToFull(tuple([self.N + 1] * q.npar), + q.npar, eV[:, 0]) + qs = qs + [copy(q)] + self.verbosity += 10 + vbMng(self, "DEL", "Done computing denominator.", 7) + return qs, fitinvP + + def _setupNumerator(self): + """Compute rational numerator.""" + RROMPyAssert(self._mode, message = "Cannot setup numerator.") + vbMng(self, "INIT", "Starting computation of numerator.", 7) + Qevaldiag = np.zeros((len(self.musPivot), len(self.musPivot)), + dtype = np.complex) + verb = self.trainedModel.verbosity + self.trainedModel.verbosity = 0 + self._setupPivotInterpolationIndices() + M0 = copy(self.M) + tensor_idx = 0 + ps = [] + for k, muM in enumerate(self.musMarginal): + self._M = M0 + idxGlob = 0 + for j, derIdxs in enumerate(self._derPIdxs): + mujEff = [fp] * self.npar + for jj, kk in enumerate(self.directionPivot): + mujEff[kk] = self._musPUnique[j, jj] + for jj, kk in enumerate(self.directionMarginal): + mujEff[kk] = muM(0, jj) + mujEff = checkParameter(mujEff, self.npar) + nder = len(derIdxs) + idxLoc = np.arange(len(self.musPivot))[ + (self._reorderP >= idxGlob) + * (self._reorderP < idxGlob + nder)] + idxGlob += nder + Qval = [0] * nder + for der in range(nder): + derIdx = hashI(der, self.musPivot.shape[1]) + derIdxEff = [0] * self.npar + sclEff = [0] * self.npar + for jj, kk in enumerate(self.directionPivot): + derIdxEff[kk] = derIdx[jj] + sclEff[kk] = self.scaleFactorPivot[jj] ** -1. + Qval[der] = (self.trainedModel.getQVal(mujEff, derIdxEff, + scl = sclEff) + / multifactorial(derIdx)) + for derU, derUIdx in enumerate(derIdxs): + for derQ, derQIdx in enumerate(derIdxs): + diffIdx = [x - y for (x, y) in zip(derUIdx, derQIdx)] + if all([x >= 0 for x in diffIdx]): + diffj = hashD(diffIdx) + Qevaldiag[idxLoc[derU], idxLoc[derQ]] = Qval[diffj] + while self.M >= 0: + if self.radialBasisPivot == 0: + p = PI() + wellCond, msg = p.setupByInterpolation( + self._musPUniqueCN, Qevaldiag, self.M, + self.polybasisPivotP, self.verbosity >= 5, True, + {"derIdxs": self._derPIdxs, + "reorder": self._reorderP, + "scl": np.power(self.scaleFactorPivot, -1.)}, + {"rcond": self.interpRcondPivot}) + else: + p = RBI() + wellCond, msg = p.setupByInterpolation( + self._musPUniqueCN, Qevaldiag, self.M, + self.polybasisPivotP, + self.radialBasisWeightsPivot, + self.verbosity >= 5, True, + {"derIdxs": self._derPIdxs, + "reorder": self._reorderP, + "scl": np.power(self.scaleFactorPivot, -1.)}, + {"rcond": self.interpRcondPivot}) + vbMng(self, "MAIN", msg, 5) + if wellCond: break + if self.catchInstability: + raise RROMPyException(("Instability in numerator " + "computation: polyfit is " + "poorly conditioned.")) + RROMPyWarning(("Polyfit is poorly conditioned. " + "Reducing M by 1.")) + self.M -= 1 + if self.M < 0: + raise RROMPyException(("Instability in computation of " + "numerator. Aborting.")) + tensor_idx_new = tensor_idx + Qevaldiag.shape[1] + if self.POD: + p.postmultiplyTensorize(self.samplingEngine.RPODCoalesced.T[ + tensor_idx : tensor_idx_new, :]) + else: + p.pad(tensor_idx, len(self.mus) - tensor_idx_new) + tensor_idx = tensor_idx_new + ps = ps + [copy(p)] + self.trainedModel.verbosity = verb + vbMng(self, "DEL", "Done computing numerator.", 7) + return ps + + def setupApprox(self): + """ + Compute rational interpolant. + SVD-based robust eigenvalue management. + """ + if self.checkComputedApprox(): + return + RROMPyAssert(self._mode, message = "Cannot setup approximant.") + vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5) + self.computeScaleFactor() + self.computeSnapshots() + if self.trainedModel is None: + self.trainedModel = tModel() + self.trainedModel.verbosity = self.verbosity + self.trainedModel.timestamp = self.timestamp + data = TrainedModelPivotedData(self.trainedModel.name(), self.mu0, + self.samplingEngine.samplesCoalesced, + self.scaleFactor, + self.HFEngine.rescalingExp, + self.directionPivot) + data.musPivot = copy(self.musPivot) + data.musMarginal = copy(self.musMarginal) + self.trainedModel.data = data + else: + self.trainedModel = self.trainedModel + self.trainedModel.data.projMat = copy(self.samplingEngine.samples) + self.trainedModel.data.marginalInterp = self._setupMarginalInterp() + if self.N > 0: + Qs = self._setupDenominator()[0] + else: + Q = PI() + Q.npar = self.musPivot.shape[1] + Q.coeffs = np.ones(tuple([1] * Q.npar), dtype = np.complex) + Q.polybasis = self.polybasisPivot + Qs = [Q for _ in range(len(self.musMarginal))] + self.trainedModel.data.Qs = Qs + self.trainedModel.data.Ps = self._setupNumerator() + self.trainedModel.data.approxParameters = copy(self.approxParameters) + vbMng(self, "DEL", "Done setting up approximant.", 5) + + def _computeInterpolantInverseBlocks(self) -> Tuple[List[Np2D], Np2D]: + """ + Compute inverse factors for minimal interpolant target functional. + """ + RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.") + self._setupPivotInterpolationIndices() + while self.E >= 0: + eWidth = (hashD([self.E + 1] + [0] * (self.musPivot.shape[1] - 1)) + - hashD([self.E] + [0] * (self.musPivot.shape[1] - 1))) + TE, _, argIdxs = hpvP(self._musPUniqueCN, self.E, + self.polybasisPivot, self._derPIdxs, + self._reorderP, + scl = np.power(self.scaleFactorPivot, -1.)) + fitOut = customPInv(TE[:, argIdxs], rcond = self.interpRcondPivot, + full = True) + vbMng(self, "MAIN", + ("Fitting {} samples with degree {} through {}... " + "Conditioning of pseudoinverse system: {:.4e}.").format( + TE.shape[0], self.E, + polyfitname(self.polybasisPivot), + fitOut[1][1][0] / fitOut[1][1][-1]), + 5) + if fitOut[1][0] == len(argIdxs): + fitinvP = fitOut[0][- eWidth : , :] + break + RROMPyWarning("Polyfit is poorly conditioned. Reducing E by 1.") + self.E -= 1 + if self.E < 0: + raise RROMPyException(("Instability in computation of " + "denominator. Aborting.")) + TN, _, argIdxs = hpvP(self._musPUniqueCN, self.N, self.polybasisPivot, + self._derPIdxs, self._reorderP, + scl = np.power(self.scaleFactorPivot, -1.)) + TN = TN[:, argIdxs] + invD = [None] * (eWidth) + for k in range(eWidth): + pseudoInv = np.diag(fitinvP[k, :]) + idxGlob = 0 + for j, derIdxs in enumerate(self._derPIdxs): + nder = len(derIdxs) + idxGlob += nder + if nder > 1: + idxLoc = np.arange(len(self.musPivot))[ + (self._reorderP >= idxGlob - nder) + * (self._reorderP < idxGlob)] + invLoc = fitinvP[k, idxLoc] + pseudoInv[np.ix_(idxLoc, idxLoc)] = 0. + for diffj, diffjIdx in enumerate(derIdxs): + for derQ, derQIdx in enumerate(derIdxs): + derUIdx = [x - y for (x, y) in + zip(diffjIdx, derQIdx)] + if all([x >= 0 for x in derUIdx]): + derU = hashD(derUIdx) + pseudoInv[idxLoc[derU], idxLoc[derQ]] = ( + invLoc[diffj]) + invD[k] = pseudoInv.dot(TN) + return invD, fitinvP + + def centerNormalize(self, mu : paramList = [], + mu0 : paramVal = None) -> paramList: + """ + Compute normalized parameter to be plugged into approximant. + + Args: + mu: Parameter(s) 1. + mu0: Parameter(s) 2. If None, set to self.mu0. + + Returns: + Normalized parameter. + """ + return self.trainedModel.centerNormalize(mu, mu0) + + def centerNormalizePivot(self, mu : paramList = [], + mu0 : paramVal = None) -> paramList: + """ + Compute normalized parameter to be plugged into approximant. + + Args: + mu: Parameter(s) 1. + mu0: Parameter(s) 2. If None, set to self.mu0. + + Returns: + Normalized parameter. + """ + return self.trainedModel.centerNormalizePivot(mu, mu0) + + def getResidues(self, *args, **kwargs) -> Np1D: + """ + Obtain approximant residues. + + Returns: + Matrix with residues as columns. + """ + return self.trainedModel.getResidues(*args, **kwargs) + diff --git a/rrompy/reduction_methods/pivoted/reduced_basis_pivoted.py b/rrompy/reduction_methods/pivoted/reduced_basis_pivoted.py new file mode 100644 index 0000000..1200f83 --- /dev/null +++ b/rrompy/reduction_methods/pivoted/reduced_basis_pivoted.py @@ -0,0 +1,285 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +from copy import deepcopy as copy +import numpy as np +from .generic_pivoted_approximant import GenericPivotedApproximant +from rrompy.hfengines.base import MarginalProxyEngine +from rrompy.reduction_methods.trained_model import (TrainedModelPivotedData, + TrainedModelPivotedReducedBasis as tModel) +from rrompy.reduction_methods.base.reduced_basis_utils import \ + projectAffineDecomposition +from rrompy.utilities.base.types import (Np1D, Np2D, List, ListAny, Tuple, + DictAny, HFEng, paramVal, sampList) +from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp +from rrompy.utilities.numerical import customPInv +from rrompy.utilities.exception_manager import (RROMPyWarning, RROMPyException, + RROMPyAssert) + +__all__ = ['ReducedBasisPivoted'] + +class ReducedBasisPivoted(GenericPivotedApproximant): + """ + ROM pivoted RB approximant computation for parametric problems. + + Args: + HFEngine: HF problem solver. + mu0(optional): Default parameter. Defaults to 0. + directionPivot(optional): Pivot components. Defaults to [0]. + approxParameters(optional): Dictionary containing values for main + parameters of approximant. Recognized keys are: + - 'POD': whether to compute POD of snapshots; defaults to True; + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator; + - 'R': rank for pivot Galerkin projection; defaults to prod(S); + - 'PODTolerance': tolerance for pivot snapshots POD; defaults to + -1; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' + and 'LEGENDRE'; defaults to 'MONOMIAL'; + - 'MMarginal': degree of marginal interpolant; defaults to 0; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; defaults to 0, i.e. no radial basis; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; defaults to 0, i.e. identity; + - 'interpRcondMarginal': tolerance for marginal interpolation; + defaults to None. + Defaults to empty dict. + homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults + to False. + verbosity(optional): Verbosity level. Defaults to 10. + + Attributes: + HFEngine: HF problem solver. + mu0: Default parameter. + directionPivot: Pivot components. + mus: Array of snapshot parameters. + homogeneized: Whether to homogeneize Dirichlet BCs. + approxRadius: Dummy radius of approximant (i.e. distance from mu0 to + farthest sample point). + approxParameters: Dictionary containing values for main parameters of + approximant. Recognized keys are in parameterList. + parameterListSoft: Recognized keys of soft approximant parameters: + - 'POD': whether to compute POD of snapshots; + - 'R': rank for Galerkin projection; + - 'PODTolerance': tolerance for snapshots POD; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; + - 'MMarginal': degree of marginal interpolant; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; + - 'interpRcondMarginal': tolerance for marginal interpolation. + parameterListCritical: Recognized keys of critical approximant + parameters: + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator. + POD: Whether to compute POD of snapshots. + S: Total number of pivot samples current approximant relies upon. + samplerPivot: Pivot sample point generator. + SMarginal: Total number of marginal samples current approximant relies + upon. + samplerMarginal: Marginal sample point generator. + R: Rank for Galerkin projection. + PODTolerance: Tolerance for pivot snapshots POD. + polybasisMarginal: Type of polynomial basis for marginal interpolation. + MMarginal: Degree of marginal interpolant. + radialBasisMarginal: Radial basis family for marginal interpolant. + radialBasisWeightsMarginal: Radial basis weights for marginal + interpolant. + interpRcondMarginal: Tolerance for marginal interpolation. + muBoundsPivot: list of bounds for pivot parameter values. + muBoundsMarginal: list of bounds for marginal parameter values. + samplingEngine: Sampling engine. + uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as + sampleList. + lastSolvedHF: Parameter(s) corresponding to last computed high fidelity + solution(s) as parameterList. + uApproxReduced: Reduced approximate solution(s) with parameter(s) + lastSolvedApprox as sampleList. + lastSolvedApproxReduced: Parameter(s) corresponding to last computed + reduced approximate solution(s) as parameterList. + uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as + sampleList. + lastSolvedApprox: Parameter(s) corresponding to last computed + approximate solution(s) as parameterList. + As: List of sparse matrices (in CSC format) representing coefficients + of linear system matrix. + bs: List of numpy vectors representing coefficients of linear system + RHS. + ARBs: List of sparse matrices (in CSC format) representing coefficients + of compressed linear system matrix. + bRBs: List of numpy vectors representing coefficients of compressed + linear system RHS. + """ + + def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, + directionPivot : ListAny = [0], + approxParameters : DictAny = {}, homogeneized : bool = False, + verbosity : int = 10, timestamp : bool = True): + self._preInit() + self._addParametersToList(["R", "PODTolerance"], [1, -1]) + super().__init__(HFEngine = HFEngine, mu0 = mu0, + directionPivot = directionPivot, + approxParameters = approxParameters, + homogeneized = homogeneized, + verbosity = verbosity, timestamp = timestamp) + self._postInit() + + @property + def S(self): + """Value of S.""" + return self._S + @S.setter + def S(self, S): + GenericPivotedApproximant.S.fset(self, S) + if not hasattr(self, "_R"): + self._R = np.prod(self.S) * np.prod(self.SMarginal) + + @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 hasattr(self, "_SMarginal") + and np.prod(self.S) * np.prod(self.SMarginal) < self.R): + RROMPyWarning(("Prescribed S and SMarginal are too small. " + "Decreasing R.")) + self.R = np.prod(self.S) * np.prod(self.SMarginal) + + @property + def PODTolerance(self): + """Value of PODTolerance.""" + return self._PODTolerance + @PODTolerance.setter + def PODTolerance(self, PODTolerance): + self._PODTolerance = PODTolerance + self._approxParameters["PODTolerance"] = self.PODTolerance + + def _setupProjectionMatrix(self): + """Compute projection matrix.""" + RROMPyAssert(self._mode, message = "Cannot setup numerator.") + vbMng(self, "INIT", "Starting computation of projection matrix.", 7) + if self.POD: + U, s, _ = np.linalg.svd(self.samplingEngine.RPODCoalesced) + s = s ** 2. + else: + Gramian = self.HFEngine.innerProduct( + self.samplingEngine.samplesCoalesced, + self.samplingEngine.samplesCoalesced) + U, s, _ = np.linalg.svd(Gramian) + nsamples = self.samplingEngine.samplesCoalesced.shape[1] + snorm = np.cumsum(s[::-1]) / np.sum(s) + nPODTrunc = min(nsamples - np.argmax(snorm > self.PODTolerance), + self.R) + pMat = self.samplingEngine.samplesCoalesced.dot(U[:, : nPODTrunc]) + vbMng(self, "MAIN", + "Assembling {}x{} projection matrix from {} samples.".format( + *(pMat.shape), nsamples), 5) + vbMng(self, "DEL", "Done computing projection matrix.", 7) + return pMat + + def _setupAffineBlocks(self): + """Compute list of marginalized affine blocks of system.""" + hasAs = hasattr(self, "AsList") and self.AsList is not None + hasbs = hasattr(self, "bsList") and self.bsList is not None + if hasAs and hasbs: return + vbMng(self, "INIT", "Computing affine blocks of system.", 10) + mu0Eff = self.mu0.data[0, self.directionPivot] + if not hasAs: self.AsList = [None] * len(self.musMarginal) + if not hasbs: self.bsList = [None] * len(self.musMarginal) + for k, muM in enumerate(self.musMarginal): + muEff = [fp] * self.npar + for jj, kk in enumerate(self.directionMarginal): + muEff[kk] = muM(0, jj) + MEnginek = MarginalProxyEngine(self.HFEngine, muEff) + if not hasAs: + self.AsList[k] = MEnginek.affineLinearSystemA(mu0Eff, + self.scaleFactorPivot) + if not hasbs: + self.bsList[k] = MEnginek.affineLinearSystemb(mu0Eff, + self.scaleFactorPivot, + self.homogeneized) + vbMng(self, "DEL", "Done computing affine blocks.", 10) + + def setupApprox(self): + """Compute RB projection matrix.""" + if self.checkComputedApprox(): + return + RROMPyAssert(self._mode, message = "Cannot setup approximant.") + vbMng(self, "INIT", "Setting up {}.".format(self.name()), 5) + self.computeScaleFactor() + self.computeSnapshots() + self._setupAffineBlocks() + pMat = self._setupProjectionMatrix() + ARBsList, bRBsList, pList = self.assembleReducedSystemMarginal(pMat) + if self.trainedModel is None: + self.trainedModel = tModel() + self.trainedModel.verbosity = self.verbosity + self.trainedModel.timestamp = self.timestamp + data = TrainedModelPivotedData(self.trainedModel.name(), self.mu0, + pList, self.scaleFactor, + self.HFEngine.rescalingExp, + self.directionPivot) + data.musPivot = copy(self.musPivot) + data.musMarginal = copy(self.musMarginal) + self.trainedModel.data = data + else: + self.trainedModel = self.trainedModel + self.trainedModel.data.projMat = copy(pList) + self.trainedModel.data.marginalInterp = self._setupMarginalInterp() + self.trainedModel.data.ARBsList = ARBsList + self.trainedModel.data.bRBsList = bRBsList + self.trainedModel.data.approxParameters = copy(self.approxParameters) + vbMng(self, "DEL", "Done setting up approximant.", 5) + + def assembleReducedSystemMarginal(self, pMat : sampList = None)\ + -> Tuple[List[List[Np2D]], + List[List[Np1D]], List[Np2D]]: + """Build affine blocks of RB linear system through projections.""" + if pMat is None: + self.setupApprox() + ARBsList = self.trainedModel.data.ARBsList + bRBsList = self.trainedModel.data.bRBsList + else: + vbMng(self, "INIT", "Projecting affine terms of HF model.", 10) + ARBsList = [None] * len(self.musMarginal) + bRBsList = [None] * len(self.musMarginal) + projList = [None] * len(self.musMarginal) + for k, (As, bs, sample) in enumerate(zip(self.AsList, self.bsList, + self.samplingEngine.samples)): + compLocal = self.HFEngine.innerProduct(sample, pMat) + projList[k] = sample.dot(customPInv(compLocal)) + ARBsList[k], bRBsList[k] = projectAffineDecomposition(As, bs, + projList[k]) + vbMng(self, "DEL", "Done projecting affine terms.", 10) + return ARBsList, bRBsList, projList + diff --git a/rrompy/reduction_methods/distributed/__init__.py b/rrompy/reduction_methods/pole_matching/__init__.py similarity index 68% copy from rrompy/reduction_methods/distributed/__init__.py copy to rrompy/reduction_methods/pole_matching/__init__.py index 1d7c1fc..0370cf1 100644 --- a/rrompy/reduction_methods/distributed/__init__.py +++ b/rrompy/reduction_methods/pole_matching/__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_distributed_approximant import GenericDistributedApproximant -from .rational_interpolant import RationalInterpolant -from .rb_distributed import RBDistributed +from .generic_pole_matching_approximant import GenericPoleMatchingApproximant +from .rational_interpolant_pole_matching import RationalInterpolantPoleMatching +from .reduced_basis_pole_matching import ReducedBasisPoleMatching __all__ = [ - 'GenericDistributedApproximant', - 'RationalInterpolant', - 'RBDistributed' + 'GenericPoleMatchingApproximant', + 'RationalInterpolantPoleMatching', + 'ReducedBasisPoleMatching' ] diff --git a/rrompy/reduction_methods/pole_matching/generic_pole_matching_approximant.py b/rrompy/reduction_methods/pole_matching/generic_pole_matching_approximant.py new file mode 100644 index 0000000..660e7d9 --- /dev/null +++ b/rrompy/reduction_methods/pole_matching/generic_pole_matching_approximant.py @@ -0,0 +1,179 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +from numpy import inf +from rrompy.reduction_methods.pivoted.generic_pivoted_approximant import ( + GenericPivotedApproximant) +from rrompy.utilities.base.types import ListAny, DictAny, HFEng, paramVal +from rrompy.utilities.exception_manager import RROMPyException, RROMPyWarning + +__all__ = ['GenericPoleMatchingApproximant'] + +class GenericPoleMatchingApproximant(GenericPivotedApproximant): + """ + ROM pole matching approximant computation for parametric problems + (ABSTRACT). + + Args: + HFEngine: HF problem solver. + mu0(optional): Default parameter. Defaults to 0. + directionPivot(optional): Pivot components. Defaults to [0]. + approxParameters(optional): Dictionary containing values for main + parameters of approximant. Recognized keys are: + - 'POD': whether to compute POD of snapshots; defaults to True; + - 'matchingWeight': weight for pole matching optimization; defaults + to 1; + - 'cutOffTolerance': tolerance for ignoring parasitic poles; + defaults to np.inf; + - 'cutOffType': rule for tolerance computation for parasitic poles; + defaults to 'MAGNITUDE'; + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' + and 'LEGENDRE'; defaults to 'MONOMIAL'; + - 'MMarginal': degree of marginal interpolant; defaults to 0; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; defaults to 0, i.e. no radial basis; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; defaults to 0, i.e. identity; + - 'interpRcondMarginal': tolerance for marginal interpolation; + defaults to None. + Defaults to empty dict. + homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults + to False. + verbosity(optional): Verbosity level. Defaults to 10. + + Attributes: + HFEngine: HF problem solver. + mu0: Default parameter. + directionPivot: Pivot components. + mus: Array of snapshot parameters. + musPivot: Array of pivot snapshot parameters. + musMarginal: Array of marginal snapshot parameters. + homogeneized: Whether to homogeneize Dirichlet BCs. + approxParameters: Dictionary containing values for main parameters of + approximant. Recognized keys are in parameterList. + parameterListSoft: Recognized keys of soft approximant parameters: + - 'POD': whether to compute POD of snapshots; + - 'matchingWeight': weight for pole matching optimization; + - 'cutOffTolerance': tolerance for ignoring parasitic poles; + - 'cutOffType': rule for tolerance computation for parasitic poles; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; + - 'MMarginal': degree of marginal interpolant; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; + - 'interpRcondMarginal': tolerance for marginal interpolation. + parameterListCritical: Recognized keys of critical approximant + parameters: + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator. + POD: Whether to compute POD of snapshots. + matchingWeight: Weight for pole matching optimization. + cutOffTolerance: Tolerance for ignoring parasitic poles. + cutOffType: Rule for tolerance computation for parasitic poles. + S: Total number of pivot samples current approximant relies upon. + samplerPivot: Pivot sample point generator. + SMarginal: Total number of marginal samples current approximant relies + upon. + samplerMarginal: Marginal sample point generator. + polybasisMarginal: Type of polynomial basis for marginal interpolation. + MMarginal: Degree of marginal interpolant. + radialBasisMarginal: Radial basis family for marginal interpolant. + radialBasisWeightsMarginal: Radial basis weights for marginal + interpolant. + interpRcondMarginal: Tolerance for marginal interpolation. + muBoundsPivot: list of bounds for pivot parameter values. + muBoundsMarginal: list of bounds for marginal parameter values. + samplingEngine: Sampling engine. + uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as + sampleList. + lastSolvedHF: Parameter(s) corresponding to last computed high fidelity + solution(s) as parameterList. + uApproxReduced: Reduced approximate solution(s) with parameter(s) + lastSolvedApprox as sampleList. + lastSolvedApproxReduced: Parameter(s) corresponding to last computed + reduced approximate solution(s) as parameterList. + uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as + sampleList. + lastSolvedApprox: Parameter(s) corresponding to last computed + approximate solution(s) as parameterList. + """ + + def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, + directionPivot : ListAny = [0], + approxParameters : DictAny = {}, homogeneized : bool = False, + verbosity : int = 10, timestamp : bool = True): + self._preInit() + if len(directionPivot) > 1: + raise RROMPyException(("Exactly 1 pivot parameter allowed in pole " + "matching.")) + self._addParametersToList(["matchingWeight", "cutOffTolerance", + "cutOffType"], [1, inf, "MAGNITUDE"]) + super().__init__(HFEngine = HFEngine, mu0 = mu0, + directionPivot = directionPivot, + approxParameters = approxParameters, + homogeneized = homogeneized, + verbosity = verbosity, timestamp = timestamp) + self._postInit() + + @property + def matchingWeight(self): + """Value of matchingWeight.""" + return self._matchingWeight + @matchingWeight.setter + def matchingWeight(self, matchingWeight): + self._matchingWeight = matchingWeight + self._approxParameters["matchingWeight"] = self.matchingWeight + + @property + def cutOffTolerance(self): + """Value of cutOffTolerance.""" + return self._cutOffTolerance + @cutOffTolerance.setter + def cutOffTolerance(self, cutOffTolerance): + self._cutOffTolerance = cutOffTolerance + self._approxParameters["cutOffTolerance"] = self.cutOffTolerance + + @property + def cutOffType(self): + """Value of cutOffType.""" + return self._cutOffType + @cutOffType.setter + def cutOffType(self, cutOffType): + try: + cutOffType = cutOffType.upper().strip().replace(" ","") + if cutOffType not in ["MAGNITUDE", "POTENTIAL"]: + raise RROMPyException("Prescribed cutOffType not recognized.") + self._cutOffType = cutOffType + except: + RROMPyWarning(("Prescribed cutOffType not recognized. Overriding " + "to 'MAGNITUDE'.")) + self._cutOffType = "MAGNITUDE" + self._approxParameters["cutOffType"] = self.cutOffType diff --git a/rrompy/reduction_methods/pole_matching/rational_interpolant_pole_matching.py b/rrompy/reduction_methods/pole_matching/rational_interpolant_pole_matching.py new file mode 100644 index 0000000..5e33c96 --- /dev/null +++ b/rrompy/reduction_methods/pole_matching/rational_interpolant_pole_matching.py @@ -0,0 +1,222 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +from copy import deepcopy as copy +import numpy as np +from rrompy.reduction_methods.pivoted.rational_interpolant_pivoted import \ + RationalInterpolantPivoted +from .generic_pole_matching_approximant import GenericPoleMatchingApproximant +from rrompy.utilities.poly_fitting.polynomial import ( + PolynomialInterpolator as PI) +from rrompy.reduction_methods.trained_model import (TrainedModelPivotedData, + TrainedModelPoleMatchingRational as tModel) +from rrompy.utilities.base import verbosityManager as vbMng +from rrompy.utilities.exception_manager import RROMPyAssert + +__all__ = ['RationalInterpolantPoleMatching'] + +class RationalInterpolantPoleMatching(GenericPoleMatchingApproximant, + RationalInterpolantPivoted): + """ + ROM pivoted rational interpolant computation for parametric problems with + pole matching. + + Args: + HFEngine: HF problem solver. + mu0(optional): Default parameter. Defaults to 0. + directionPivot(optional): Pivot components. Defaults to [0]. + approxParameters(optional): Dictionary containing values for main + parameters of approximant. Recognized keys are: + - 'POD': whether to compute POD of snapshots; defaults to True; + - 'matchingWeight': weight for pole matching optimization; defaults + to 1; + - 'cutOffTolerance': tolerance for ignoring parasitic poles; + defaults to np.inf; + - 'cutOffType': rule for tolerance computation for parasitic poles; + defaults to 'MAGNITUDE'; + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator; + - 'polybasisPivot': type of polynomial basis for pivot + interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' + and 'LEGENDRE'; defaults to 'MONOMIAL'; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' + and 'LEGENDRE'; defaults to 'MONOMIAL'; + - 'E': number of derivatives used to compute interpolant; defaults + to 0; + - 'M': degree of rational interpolant numerator; defaults to 0; + - 'N': degree of rational interpolant denominator; defaults to 0; + - 'MMarginal': degree of marginal interpolant; defaults to 0; + - 'radialBasisPivot': radial basis family for pivot numerator; + defaults to 0, i.e. no radial basis; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; defaults to 0, i.e. no radial basis; + - 'radialBasisWeightsPivot': radial basis weights for pivot + numerator; defaults to 0, i.e. identity; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; defaults to 0, i.e. identity; + - 'interpRcondPivot': tolerance for pivot interpolation; defaults + to None; + - 'interpRcondMarginal': tolerance for marginal interpolation; + defaults to None; + - 'robustTol': tolerance for robust rational denominator + management; defaults to 0. + Defaults to empty dict. + 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. + directionPivot: Pivot components. + mus: Array of snapshot parameters. + musPivot: Array of pivot snapshot parameters. + musMarginal: Array of marginal snapshot parameters. + homogeneized: Whether to homogeneize Dirichlet BCs. + approxParameters: Dictionary containing values for main parameters of + approximant. Recognized keys are in parameterList. + parameterListSoft: Recognized keys of soft approximant parameters: + - 'POD': whether to compute POD of snapshots; + - 'matchingWeight': weight for pole matching optimization; + - 'cutOffTolerance': tolerance for ignoring parasitic poles; + - 'cutOffType': rule for tolerance computation for parasitic poles; + - 'polybasisPivot': type of polynomial basis for pivot + interpolation; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; + - 'E': number of derivatives used to compute interpolant; + - 'M': degree of rational interpolant numerator; + - 'N': degree of rational interpolant denominator; + - 'MMarginal': degree of marginal interpolant; + - 'radialBasisPivot': radial basis family for pivot numerator; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; + - 'radialBasisWeightsPivot': radial basis weights for pivot + numerator; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; + - 'interpRcondPivot': tolerance for pivot interpolation; + - 'interpRcondMarginal': tolerance for marginal interpolation; + - 'robustTol': tolerance for robust rational denominator + management. + parameterListCritical: Recognized keys of critical approximant + parameters: + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator. + POD: Whether to compute POD of snapshots. + matchingWeight: Weight for pole matching optimization. + cutOffTolerance: Tolerance for ignoring parasitic poles. + cutOffType: Rule for tolerance computation for parasitic poles. + S: Total number of pivot samples current approximant relies upon. + sampler: Pivot sample point generator. + polybasisPivot: Type of polynomial basis for pivot interpolation. + polybasisMarginal: Type of polynomial basis for marginal interpolation. + M: Numerator degree of approximant. + N: Denominator degree of approximant. + MMarginal: Degree of marginal interpolant. + radialBasisPivot: Radial basis family for pivot numerator. + radialBasisMarginal: Radial basis family for marginal interpolant. + radialBasisWeightsPivot: Radial basis weights for pivot numerator. + radialBasisWeightsMarginal: Radial basis weights for marginal + interpolant. + interpRcondPivot: Tolerance for pivot interpolation. + interpRcondMarginal: Tolerance for marginal interpolation. + robustTol: Tolerance for robust rational denominator management. + E: Complete derivative depth level of S. + muBoundsPivot: list of bounds for pivot parameter values. + muBoundsMarginal: list of bounds for marginal parameter values. + samplingEngine: Sampling engine. + uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as + sampleList. + lastSolvedHF: Parameter(s) corresponding to last computed high fidelity + solution(s) as parameterList. + uApproxReduced: Reduced approximate solution(s) with parameter(s) + lastSolvedApprox as sampleList. + lastSolvedApproxReduced: Parameter(s) corresponding to last computed + reduced approximate solution(s) as parameterList. + uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as + sampleList. + lastSolvedApprox: Parameter(s) corresponding to last computed + approximate solution(s) as parameterList. + Q: Numpy 1D vector containing complex coefficients of approximant + denominator. + P: Numpy 2D vector whose columns are FE dofs of coefficients of + approximant numerator. + """ + + def setupApprox(self): + """ + Compute rational interpolant. + SVD-based robust eigenvalue management. + """ + if self.checkComputedApprox(): + return + RROMPyAssert(self._mode, message = "Cannot setup approximant.") + vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5) + self.computeScaleFactor() + self.computeSnapshots() + if self.trainedModel is None: + self.trainedModel = tModel() + self.trainedModel.verbosity = self.verbosity + self.trainedModel.timestamp = self.timestamp + data = TrainedModelPivotedData(self.trainedModel.name(), self.mu0, + self.samplingEngine.samplesCoalesced, + self.scaleFactor, + self.HFEngine.rescalingExp, + self.directionPivot) + data.musPivot = copy(self.musPivot) + data.musMarginal = copy(self.musMarginal) + self.trainedModel.data = data + else: + self.trainedModel = self.trainedModel + self.trainedModel.data.projMat = copy( + self.samplingEngine.samplesCoalesced) + self.trainedModel.data.marginalInterp = self._setupMarginalInterp() + if self.N > 0: + Qs = self._setupDenominator()[0] + else: + Q = PI() + Q.npar = self.musPivot.shape[1] + Q.coeffs = np.ones(tuple([1] * Q.npar), dtype = np.complex) + Q.polybasis = self.polybasisPivot + Qs = [Q for _ in range(len(self.musMarginal))] + self.trainedModel.data._temporary = True + self.trainedModel.data.Qs = Qs + self.trainedModel.data.Ps = self._setupNumerator() + vbMng(self, "INIT", "Matching poles.", 10) + self.trainedModel.initializeFromRational(self.HFEngine, + self.matchingWeight, self.POD) + vbMng(self, "DEL", "Done matching poles.", 10) + if not np.isinf(self.cutOffTolerance): + vbMng(self, "INIT", "Recompressing by cut-off.", 10) + msg = self.trainedModel.recompressByCutOff([-1., 1.], + self.cutOffTolerance, + self.cutOffType) + vbMng(self, "DEL", "Done recompressing." + msg, 10) + self.trainedModel.data.approxParameters = copy(self.approxParameters) + vbMng(self, "DEL", "Done setting up approximant.", 5) + diff --git a/rrompy/reduction_methods/pole_matching/reduced_basis_pole_matching.py b/rrompy/reduction_methods/pole_matching/reduced_basis_pole_matching.py new file mode 100644 index 0000000..4cf1d65 --- /dev/null +++ b/rrompy/reduction_methods/pole_matching/reduced_basis_pole_matching.py @@ -0,0 +1,190 @@ +# 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 isinf +from copy import deepcopy as copy +from rrompy.reduction_methods.pivoted.reduced_basis_pivoted import \ + ReducedBasisPivoted +from .generic_pole_matching_approximant import GenericPoleMatchingApproximant +from rrompy.reduction_methods.trained_model import (TrainedModelPivotedData, + TrainedModelPoleMatchingReducedBasis as tModel) +from rrompy.utilities.base import verbosityManager as vbMng +from rrompy.utilities.exception_manager import RROMPyAssert + +__all__ = ['ReducedBasisPoleMatching'] + +class ReducedBasisPoleMatching(GenericPoleMatchingApproximant, + ReducedBasisPivoted): + """ + ROM pivoted RB approximant computation for parametric problems with pole + matching. + + Args: + HFEngine: HF problem solver. + mu0(optional): Default parameter. Defaults to 0. + directionPivot(optional): Pivot components. Defaults to [0]. + approxParameters(optional): Dictionary containing values for main + parameters of approximant. Recognized keys are: + - 'POD': whether to compute POD of snapshots; defaults to True; + - 'matchingWeight': weight for pole matching optimization; defaults + to 1; + - 'cutOffTolerance': tolerance for ignoring parasitic poles; + defaults to np.inf; + - 'cutOffType': rule for tolerance computation for parasitic poles; + defaults to 'MAGNITUDE'; + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator; + - 'R': rank for pivot Galerkin projection; defaults to prod(S); + - 'PODTolerance': tolerance for pivot snapshots POD; defaults to + -1; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' + and 'LEGENDRE'; defaults to 'MONOMIAL'; + - 'MMarginal': degree of marginal interpolant; defaults to 0; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; defaults to 0, i.e. no radial basis; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; defaults to 0, i.e. identity; + - 'interpRcondMarginal': tolerance for marginal interpolation; + defaults to None. + Defaults to empty dict. + homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults + to False. + verbosity(optional): Verbosity level. Defaults to 10. + + Attributes: + HFEngine: HF problem solver. + mu0: Default parameter. + directionPivot: Pivot components. + mus: Array of snapshot parameters. + musPivot: Array of pivot snapshot parameters. + musMarginal: Array of marginal snapshot parameters. + homogeneized: Whether to homogeneize Dirichlet BCs. + approxParameters: Dictionary containing values for main parameters of + approximant. Recognized keys are in parameterList. + parameterListSoft: Recognized keys of soft approximant parameters: + - 'POD': whether to compute POD of snapshots; + - 'matchingWeight': weight for pole matching optimization; + - 'cutOffTolerance': tolerance for ignoring parasitic poles; + - 'cutOffType': rule for tolerance computation for parasitic poles; + - 'R': rank for Galerkin projection; + - 'PODTolerance': tolerance for snapshots POD; + - 'polybasisMarginal': type of polynomial basis for marginal + interpolation; + - 'MMarginal': degree of marginal interpolant; + - 'radialBasisMarginal': radial basis family for marginal + interpolant; + - 'radialBasisWeightsMarginal': radial basis weights for marginal + interpolant; + - 'interpRcondMarginal': tolerance for marginal interpolation. + parameterListCritical: Recognized keys of critical approximant + parameters: + - 'S': total number of pivot samples current approximant relies + upon; + - 'samplerPivot': pivot sample point generator; + - 'SMarginal': total number of marginal samples current approximant + relies upon; + - 'samplerMarginal': marginal sample point generator. + POD: Whether to compute POD of snapshots. + matchingWeight: Weight for pole matching optimization. + cutOffTolerance: Tolerance for ignoring parasitic poles. + cutOffType: Rule for tolerance computation for parasitic poles. + S: Total number of pivot samples current approximant relies upon. + samplerPivot: Pivot sample point generator. + SMarginal: Total number of marginal samples current approximant relies + upon. + samplerMarginal: Marginal sample point generator. + R: Rank for Galerkin projection. + PODTolerance: Tolerance for pivot snapshots POD. + polybasisMarginal: Type of polynomial basis for marginal interpolation. + MMarginal: Degree of marginal interpolant. + radialBasisMarginal: Radial basis family for marginal interpolant. + radialBasisWeightsMarginal: Radial basis weights for marginal + interpolant. + interpRcondMarginal: Tolerance for marginal interpolation. + muBoundsPivot: list of bounds for pivot parameter values. + muBoundsMarginal: list of bounds for marginal parameter values. + samplingEngine: Sampling engine. + uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as + sampleList. + lastSolvedHF: Parameter(s) corresponding to last computed high fidelity + solution(s) as parameterList. + lastSolvedHF: Parameter(s) corresponding to last computed high fidelity + solution(s) as parameterList. + uApproxReduced: Reduced approximate solution(s) with parameter(s) + lastSolvedApprox as sampleList. + lastSolvedApproxReduced: Parameter(s) corresponding to last computed + reduced approximate solution(s) as parameterList. + uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as + sampleList. + lastSolvedApprox: Parameter(s) corresponding to last computed + approximate solution(s) as parameterList. + As: List of sparse matrices (in CSC format) representing coefficients + of linear system matrix. + bs: List of numpy vectors representing coefficients of linear system + RHS. + ARBs: List of sparse matrices (in CSC format) representing coefficients + of compressed linear system matrix. + bRBs: List of numpy vectors representing coefficients of compressed + linear system RHS. + """ + + def setupApprox(self): + """Compute RB projection matrix.""" + if self.checkComputedApprox(): + return + RROMPyAssert(self._mode, message = "Cannot setup approximant.") + vbMng(self, "INIT", "Setting up {}.".format(self.name()), 5) + self.computeScaleFactor() + self.computeSnapshots() + self._setupAffineBlocks() + pMat = self._setupProjectionMatrix() + ARBsList, bRBsList, pList = self.assembleReducedSystemMarginal(pMat) + if self.trainedModel is None: + self.trainedModel = tModel() + self.trainedModel.verbosity = self.verbosity + self.trainedModel.timestamp = self.timestamp + data = TrainedModelPivotedData(self.trainedModel.name(), self.mu0, + pList, self.scaleFactor, + self.HFEngine.rescalingExp, + self.directionPivot) + data.musPivot = copy(self.musPivot) + data.musMarginal = copy(self.musMarginal) + self.trainedModel.data = data + else: + self.trainedModel = self.trainedModel + self.trainedModel.data.projMat = copy(pList) + self.trainedModel.data.marginalInterp = self._setupMarginalInterp() + self.trainedModel.data.ARBsList = ARBsList + self.trainedModel.data.bRBsList = bRBsList + vbMng(self, "INIT", "Matching poles.", 10) + self.trainedModel.initializeFromAffine(self.HFEngine, + self.matchingWeight, self.POD) + vbMng(self, "DEL", "Done matching poles.", 10) + if not isinf(self.cutOffTolerance): + vbMng(self, "INIT", "Recompressing by cut-off.", 10) + msg = self.trainedModel.recompressByCutOff([- 1., 1.], + self.cutOffTolerance, + self.cutOffType) + vbMng(self, "DEL", "Done recompressing." + msg, 10) + self.trainedModel.data.approxParameters = copy(self.approxParameters) + vbMng(self, "DEL", "Done setting up approximant.", 5) diff --git a/rrompy/reduction_methods/distributed/__init__.py b/rrompy/reduction_methods/standard/__init__.py similarity index 81% rename from rrompy/reduction_methods/distributed/__init__.py rename to rrompy/reduction_methods/standard/__init__.py index 1d7c1fc..0ddf98c 100644 --- a/rrompy/reduction_methods/distributed/__init__.py +++ b/rrompy/reduction_methods/standard/__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_distributed_approximant import GenericDistributedApproximant +from .generic_standard_approximant import GenericStandardApproximant from .rational_interpolant import RationalInterpolant -from .rb_distributed import RBDistributed +from .reduced_basis import ReducedBasis __all__ = [ - 'GenericDistributedApproximant', + 'GenericStandardApproximant', 'RationalInterpolant', - 'RBDistributed' + 'ReducedBasis' ] diff --git a/rrompy/reduction_methods/distributed/generic_distributed_approximant.py b/rrompy/reduction_methods/standard/generic_standard_approximant.py similarity index 92% rename from rrompy/reduction_methods/distributed/generic_distributed_approximant.py rename to rrompy/reduction_methods/standard/generic_standard_approximant.py index 11c620f..7ce4a2e 100644 --- a/rrompy/reduction_methods/distributed/generic_distributed_approximant.py +++ b/rrompy/reduction_methods/standard/generic_standard_approximant.py @@ -1,167 +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 from copy import deepcopy as copy from rrompy.reduction_methods.base.generic_approximant import ( GenericApproximant) from rrompy.utilities.base.types import DictAny, HFEng, paramVal, paramList -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert from rrompy.parameter import checkParameterList -__all__ = ['GenericDistributedApproximant'] +__all__ = ['GenericStandardApproximant'] -class GenericDistributedApproximant(GenericApproximant): +class GenericStandardApproximant(GenericApproximant): """ ROM interpolant computation for parametric problems (ABSTRACT). Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator. Defaults to empty dict. homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults to False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. homogeneized: Whether to homogeneize Dirichlet BCs. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator. POD: Whether to compute POD of snapshots. S: Number of solution snapshots over which current approximant is based upon. sampler: Sample point generator. muBounds: list of bounds for parameter values. samplingEngine: Sampling engine. uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as sampleList. lastSolvedHF: Parameter(s) corresponding to last computed high fidelity solution(s) as parameterList. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApprox: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. """ def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() from rrompy.parameter.parameter_sampling import QuadratureSampler as QS self._addParametersToList([], [], ["sampler"], [QS([[0], [1]], "UNIFORM")]) del QS super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) self._postInit() @property def mus(self): """Value of mus. Its assignment may reset snapshots.""" return self._mus @mus.setter def mus(self, mus): - mus, _ = checkParameterList(mus, self.npar) + mus = checkParameterList(mus, self.npar)[0] musOld = copy(self.mus) if hasattr(self, '_mus') else None if (musOld is None or len(mus) != len(musOld) or not mus == musOld): self.resetSamples() self._mus = mus @property def muBounds(self): """Value of muBounds.""" return self.sampler.lims @property def sampler(self): """Value of sampler.""" return self._sampler @sampler.setter def sampler(self, sampler): if 'generatePoints' not in dir(sampler): raise RROMPyException("Sampler type not recognized.") if hasattr(self, '_sampler') and self._sampler is not None: samplerOld = self.sampler self._sampler = sampler self._approxParameters["sampler"] = self.sampler.__str__() if not 'samplerOld' in locals() or samplerOld != self.sampler: self.resetSamples() def setSamples(self, samplingEngine): """Copy samplingEngine and samples.""" super().setSamples(samplingEngine) self.mus = copy(self.samplingEngine.mus) def computeSnapshots(self): """Compute snapshots of solution map.""" RROMPyAssert(self._mode, message = "Cannot start snapshot computation.") if self.samplingEngine.nsamples != np.prod(self.S): - if self.verbosity >= 5: - verbosityDepth("INIT", "Starting computation of snapshots.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Starting computation of snapshots.", 5) self.mus = self.sampler.generatePoints(self.S) self.samplingEngine.iterSample(self.mus, homogeneized = self.homogeneized) - if self.verbosity >= 5: - verbosityDepth("DEL", "Done computing snapshots.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done computing snapshots.", 5) def normApprox(self, mu:paramList, homogeneized : bool = False) -> float: """ Compute norm of approximant at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Target norm of approximant. """ if not self.POD or self.homogeneized != homogeneized: return super().normApprox(mu, homogeneized) return np.linalg.norm(self.getApproxReduced(mu).data, axis = 0) def computeScaleFactor(self): """Compute parameter rescaling factor.""" RROMPyAssert(self._mode, message = "Cannot compute rescaling factor.") self.scaleFactor = .5 * np.abs( self.muBounds[0] ** self.HFEngine.rescalingExp - self.muBounds[1] ** self.HFEngine.rescalingExp) diff --git a/rrompy/reduction_methods/distributed/rational_interpolant.py b/rrompy/reduction_methods/standard/rational_interpolant.py similarity index 69% rename from rrompy/reduction_methods/distributed/rational_interpolant.py rename to rrompy/reduction_methods/standard/rational_interpolant.py index 64846fc..434f291 100644 --- a/rrompy/reduction_methods/distributed/rational_interpolant.py +++ b/rrompy/reduction_methods/standard/rational_interpolant.py @@ -1,611 +1,559 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import deepcopy as copy import numpy as np from rrompy.reduction_methods.base import checkRobustTolerance -from .generic_distributed_approximant import GenericDistributedApproximant -from rrompy.utilities.poly_fitting import customFit, customPInv +from .generic_standard_approximant import GenericStandardApproximant from rrompy.utilities.poly_fitting.polynomial import (polybases, polyfitname, nextDerivativeIndices, hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI, homogeneizedpolyvander as hpvP, - homogeneizedToFull) + homogeneizedToFull, + PolynomialInterpolator as PI) from rrompy.utilities.poly_fitting.radial_basis import (rbbases, - radialFunction, - polyfitname as polyfitnameR, - homogeneizedpolyvander as hpvR) -from rrompy.reduction_methods.trained_model import TrainedModelPade as tModel + RadialBasisInterpolator as RBI) +from rrompy.reduction_methods.trained_model import ( + TrainedModelRational as tModel) from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.utilities.base.types import (Np1D, Np2D, HFEng, DictAny, Tuple, List, paramVal, paramList, sampList) -from rrompy.utilities.base import verbosityDepth, multifactorial +from rrompy.utilities.base import verbosityManager as vbMng +from rrompy.utilities.numerical import multifactorial, customPInv from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, RROMPyWarning) __all__ = ['RationalInterpolant'] -class RationalInterpolant(GenericDistributedApproximant): +class RationalInterpolant(GenericStandardApproximant): """ ROM rational interpolant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator; - 'polybasis': type of polynomial basis for interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults to 'MONOMIAL'; - 'E': number of derivatives used to compute interpolant; defaults to 0; - - 'M': degree of Pade' interpolant numerator; defaults to 0; - - 'N': degree of Pade' interpolant denominator; defaults to 0; + - 'M': degree of rational interpolant numerator; defaults to 0; + - 'N': degree of rational interpolant denominator; defaults to 0; - 'radialBasis': radial basis family for interpolant numerator; defaults to 0, i.e. no radial basis; - 'radialBasisWeights': radial basis weights for interpolant numerator; defaults to 0, i.e. identity; - 'interpRcond': tolerance for interpolation; defaults to None; - - 'robustTol': tolerance for robust Pade' denominator management; - defaults to 0. + - 'robustTol': tolerance for robust rational denominator + management; defaults to 0. Defaults to empty dict. homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults to False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. homogeneized: Whether to homogeneize Dirichlet BCs. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots; - 'polybasis': type of polynomial basis for interpolation; - 'E': number of derivatives used to compute interpolant; - - 'M': degree of Pade' interpolant numerator; - - 'N': degree of Pade' interpolant denominator; + - 'M': degree of rational interpolant numerator; + - 'N': degree of rational interpolant denominator; - 'radialBasis': radial basis family for interpolant numerator; - 'radialBasisWeights': radial basis weights for interpolant numerator; - 'interpRcond': tolerance for interpolation via numpy.polyfit; - - 'robustTol': tolerance for robust Pade' denominator management. + - 'robustTol': tolerance for robust rational denominator + management. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator. POD: Whether to compute POD of snapshots. S: Number of solution snapshots over which current approximant is based upon. sampler: Sample point generator. polybasis: type of polynomial basis for interpolation. M: Numerator degree of approximant. N: Denominator degree of approximant. radialBasis: Radial basis family for interpolant numerator. radialBasisWeights: Radial basis weights for interpolant numerator. interpRcond: Tolerance for interpolation via numpy.polyfit. - robustTol: Tolerance for robust Pade' denominator management. + robustTol: Tolerance for robust rational denominator management. E: Complete derivative depth level of S. muBounds: list of bounds for parameter values. samplingEngine: Sampling engine. uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as sampleList. lastSolvedHF: Parameter(s) corresponding to last computed high fidelity solution(s) as parameterList. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApprox: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. Q: Numpy 1D vector containing complex coefficients of approximant denominator. P: Numpy 2D vector whose columns are FE dofs of coefficients of approximant numerator. """ def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["polybasis", "E", "M", "N", "radialBasis", "radialBasisWeights", "interpRcond", "robustTol"], ["MONOMIAL", -1, 0, 0, 0, 1, -1, 0]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) + self.catchInstability = False self._postInit() @property def polybasis(self): """Value of polybasis.""" return self._polybasis @polybasis.setter def polybasis(self, polybasis): try: polybasis = polybasis.upper().strip().replace(" ","") if polybasis not in polybases: raise RROMPyException("Prescribed polybasis not recognized.") self._polybasis = polybasis except: RROMPyWarning(("Prescribed polybasis not recognized. Overriding " "to 'MONOMIAL'.")) self._polybasis = "MONOMIAL" self._approxParameters["polybasis"] = self.polybasis @property def radialBasis(self): """Value of radialBasis.""" return self._radialBasis @radialBasis.setter def radialBasis(self, radialBasis): try: if radialBasis != 0: radialBasis = radialBasis.upper().strip().replace(" ","") if radialBasis not in rbbases: raise RROMPyException(("Prescribed radialBasis not " "recognized.")) self._radialBasis = radialBasis except: RROMPyWarning(("Prescribed radialBasis not recognized. Overriding " "to 0.")) self._radialBasis = 0 self._approxParameters["radialBasis"] = self.radialBasis @property def polybasisP(self): if self.radialBasis == 0: return self._polybasis return self._polybasis + "_" + self.radialBasis @property def interpRcond(self): """Value of interpRcond.""" return self._interpRcond @interpRcond.setter def interpRcond(self, interpRcond): self._interpRcond = interpRcond self._approxParameters["interpRcond"] = self.interpRcond @property def radialBasisWeights(self): """Value of radialBasisWeights.""" return self._radialBasisWeights @radialBasisWeights.setter def radialBasisWeights(self, radialBasisWeights): self._radialBasisWeights = radialBasisWeights self._approxParameters["radialBasisWeights"] = self.radialBasisWeights @property def M(self): """Value of M. Its assignment may change S.""" return self._M @M.setter def M(self, M): if M < 0: raise RROMPyException("M must be non-negative.") self._M = M self._approxParameters["M"] = self.M if hasattr(self, "_E") and self.E >= 0 and self.E < self.M: RROMPyWarning("Prescribed S is too small. Decreasing M.") self.M = self.E @property def N(self): """Value of N. Its assignment may change S.""" return self._N @N.setter def N(self, N): if N < 0: raise RROMPyException("N must be non-negative.") self._N = N self._approxParameters["N"] = self.N if hasattr(self, "_E") and self.E >= 0 and self.E < self.N: RROMPyWarning("Prescribed S is too small. Decreasing N.") self.N = self.E @property def E(self): """Value of E.""" return self._E @E.setter def E(self, E): if E < 0: if not hasattr(self, "_S"): raise RROMPyException(("Value of E must be positive if S is " "not yet assigned.")) E = np.sum(hashI(np.prod(self.S), self.npar)) - 1 self._E = E self._approxParameters["E"] = self.E if (hasattr(self, "_S") and self.E >= np.sum(hashI(np.prod(self.S), self.npar))): RROMPyWarning("Prescribed S is too small. Decreasing E.") self.E = -1 if hasattr(self, "_M"): self.M = self.M if hasattr(self, "_N"): self.N = self.N @property def robustTol(self): - """Value of tolerance for robust Pade' denominator management.""" + """Value of tolerance for robust rational denominator management.""" return self._robustTol @robustTol.setter def robustTol(self, robustTol): if robustTol < 0.: RROMPyWarning(("Overriding prescribed negative robustness " "tolerance to 0.")) robustTol = 0. self._robustTol = robustTol self._approxParameters["robustTol"] = self.robustTol @property def S(self): """Value of S.""" return self._S @S.setter def S(self, S): - GenericDistributedApproximant.S.fset(self, S) + GenericStandardApproximant.S.fset(self, S) if hasattr(self, "_M"): self.M = self.M if hasattr(self, "_N"): self.N = self.N if hasattr(self, "_E"): self.E = self.E def resetSamples(self): """Reset samples.""" super().resetSamples() self._musUniqueCN = None self._derIdxs = None self._reorder = None def _setupInterpolationIndices(self): """Setup parameters for polyvander.""" RROMPyAssert(self._mode, message = "Cannot setup interpolation indices.") if self._musUniqueCN is None or len(self._reorder) != len(self.mus): self._musUniqueCN, musIdxsTo, musIdxs, musCount = ( self.centerNormalize(self.mus).unique(return_index = True, return_inverse = True, return_counts = True)) self._musUnique = self.mus[musIdxsTo] self._derIdxs = [None] * len(self._musUniqueCN) self._reorder = np.empty(len(musIdxs), dtype = int) filled = 0 for j, cnt in enumerate(musCount): self._derIdxs[j] = nextDerivativeIndices([], self.mus.shape[1], cnt) jIdx = np.nonzero(musIdxs == j)[0] self._reorder[jIdx] = np.arange(filled, filled + cnt) filled += cnt def _setupDenominator(self): - """Compute Pade' denominator.""" + """Compute rational denominator.""" RROMPyAssert(self._mode, message = "Cannot setup denominator.") - if self.verbosity >= 7: - verbosityDepth("INIT", "Starting computation of denominator.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Starting computation of denominator.", 7) while self.N > 0: - invD = self._computeInterpolantInverseBlocks() + invD, fitinv = self._computeInterpolantInverseBlocks() if self.POD: ev, eV = self.findeveVGQR(self.samplingEngine.RPOD, invD) else: ev, eV = self.findeveVGExplicit(self.samplingEngine.samples, invD) - newParams = checkRobustTolerance(ev, self.N, self.robustTol) - if not newParams: - break - self.approxParameters = newParams + nevBad = checkRobustTolerance(ev, self.robustTol) + if nevBad <= 1: break + if self.catchInstability: + raise RROMPyException(("Instability in denominator " + "computation: eigenproblem is poorly " + "conditioned.")) + RROMPyWarning(("Smallest {} eigenvalues below tolerance. Reducing " + "N by 1.").format(nevBad)) + self.N = self.N - 1 if self.N <= 0: self._N = 0 eV = np.ones((1, 1)) - q = homogeneizedToFull(tuple([self.N + 1] * self.npar), self.npar, - eV[:, 0]) - if self.verbosity >= 7: - verbosityDepth("DEL", "Done computing denominator.", - timestamp = self.timestamp) - return q + q = PI() + q.npar = self.npar + q.polybasis = self.polybasis + q.coeffs = homogeneizedToFull(tuple([self.N + 1] * self.npar), + self.npar, eV[:, 0]) + vbMng(self, "DEL", "Done computing denominator.", 7) + return q, fitinv def _setupNumerator(self): - """Compute Pade' numerator.""" + """Compute rational numerator.""" RROMPyAssert(self._mode, message = "Cannot setup numerator.") - if self.verbosity >= 7: - verbosityDepth("INIT", "Starting computation of numerator.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Starting computation of numerator.", 7) Qevaldiag = np.zeros((len(self.mus), len(self.mus)), dtype = np.complex) verb = self.trainedModel.verbosity self.trainedModel.verbosity = 0 self._setupInterpolationIndices() idxGlob = 0 for j, derIdxs in enumerate(self._derIdxs): nder = len(derIdxs) idxLoc = np.arange(len(self.mus))[(self._reorder >= idxGlob) * (self._reorder < idxGlob + nder)] idxGlob += nder Qval = [0] * nder for der in range(nder): derIdx = hashI(der, self.npar) Qval[der] = (self.trainedModel.getQVal( self._musUnique[j], derIdx, scl = np.power(self.scaleFactor, -1.)) / multifactorial(derIdx)) for derU, derUIdx in enumerate(derIdxs): for derQ, derQIdx in enumerate(derIdxs): diffIdx = [x - y for (x, y) in zip(derUIdx, derQIdx)] if all([x >= 0 for x in diffIdx]): diffj = hashD(diffIdx) Qevaldiag[idxLoc[derU], idxLoc[derQ]] = Qval[diffj] if self.POD: Qevaldiag = Qevaldiag.dot(self.samplingEngine.RPOD.T) self.trainedModel.verbosity = verb while self.M >= 0: if self.radialBasis == 0: - fitVander, _, argIdxs = hpvP(self._musUniqueCN, self.M, - self.polybasisP, self._derIdxs, - self._reorder, - scl = np.power(self.scaleFactor, -1.)) - fitnameEff = polyfitname(self.polybasisP) - nsamplesPrint = "{}".format(len(fitVander)) + p = PI() + wellCond, msg = p.setupByInterpolation( + self._musUniqueCN, Qevaldiag, self.M, + self.polybasisP, self.verbosity >= 5, True, + {"derIdxs": self._derIdxs, "reorder": self._reorder, + "scl": np.power(self.scaleFactor, -1.)}, + {"rcond": self.interpRcond}) else: - fitVander, _, argIdxs = hpvR(self._musUniqueCN, self.M, - self.polybasisP, self._derIdxs, - self._reorder, self.radialBasisWeights, - scl = np.power(self.scaleFactor, -1.)) - fitnameEff = polyfitnameR(self.polybasisP) - nConstraints = len(fitVander) - len(Qevaldiag) - if nConstraints > 0: - Qevaldiag = np.pad(Qevaldiag, ((0, nConstraints), (0, 0)), - "constant") - elif nConstraints < 0: - Qevaldiag = Qevaldiag[: len(fitVander)] - fitVander = fitVander[argIdxs] - nsamplesPrint = "{}+{}".format(len(self.mus), - len(fitVander) - len(self.mus)) - fitVander = fitVander[:, argIdxs] - fitOut = customFit(fitVander, Qevaldiag, full = True, - rcond = self.interpRcond) - if self.verbosity >= 5: - condfit = fitOut[1][2][0] / fitOut[1][2][-1] - verbosityDepth("MAIN", ("Fitting {} samples with degree {} " - "through {}... Conditioning of LS " - "system: {:.4e}.").format( - nsamplesPrint, self.M, - fitnameEff, condfit), - timestamp = self.timestamp) - if fitOut[1][1] == fitVander.shape[1]: - P = fitOut[0] - break + p = RBI() + wellCond, msg = p.setupByInterpolation( + self._musUniqueCN, Qevaldiag, self.M, self.polybasisP, + self.radialBasisWeights, self.verbosity >= 5, True, + {"derIdxs": self._derIdxs, "reorder": self._reorder, + "scl": np.power(self.scaleFactor, -1.)}, + {"rcond": self.interpRcond}) + vbMng(self, "MAIN", msg, 5) + if wellCond: break + if self.catchInstability: + raise RROMPyException(("Instability in numerator computation: " + "polyfit is poorly conditioned.")) RROMPyWarning("Polyfit is poorly conditioned. Reducing M by 1.") self.M -= 1 if self.M < 0: raise RROMPyException(("Instability in computation of numerator. " "Aborting.")) - if self.radialBasis == 0: - p = homogeneizedToFull(tuple([self.M + 1] * self.npar) - + (P.shape[1],), self.npar, P) - else: - pGlob = homogeneizedToFull( - tuple([self.M + 1] * self.npar) + (P.shape[1],), - self.npar, P[len(self.mus) :]) - p = radialFunction(self._musUniqueCN[self._reorder], - self.radialBasisWeights, - P[: len(self.mus)], pGlob) - if self.verbosity >= 7: - verbosityDepth("DEL", "Done computing numerator.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done computing numerator.", 7) return p def setupApprox(self): """ - Compute Pade' interpolant. + Compute rational interpolant. SVD-based robust eigenvalue management. """ if self.checkComputedApprox(): return RROMPyAssert(self._mode, message = "Cannot setup approximant.") - if self.verbosity >= 5: - verbosityDepth("INIT", "Setting up {}.". format(self.name()), - timestamp = self.timestamp) + vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5) self.computeScaleFactor() self.computeSnapshots() if self.trainedModel is None: self.trainedModel = tModel() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp data = TrainedModelData(self.trainedModel.name(), self.mu0, self.samplingEngine.samples, + self.scaleFactor, self.HFEngine.rescalingExp) - data.polytype = self.polybasis - data.polytypeP = self.polybasisP - data.scaleFactor = self.scaleFactor data.mus = copy(self.mus) self.trainedModel.data = data else: self.trainedModel = self.trainedModel self.trainedModel.data.projMat = copy(self.samplingEngine.samples) if self.N > 0: - Q = self._setupDenominator() + Q = self._setupDenominator()[0] else: - Q = np.ones(tuple([1] * self.npar), dtype = np.complex) - self.trainedModel.data.Q = copy(Q) - self.trainedModel.data.P = copy(self._setupNumerator()) + Q = PI() + Q.coeffs = np.ones(tuple([1] * self.npar), dtype = np.complex) + Q.npar = self.npar + Q.polybasis = self.polybasis + self.trainedModel.data.Q = Q + self.trainedModel.data.P = self._setupNumerator() self.trainedModel.data.approxParameters = copy(self.approxParameters) - if self.verbosity >= 5: - verbosityDepth("DEL", "Done setting up approximant.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done setting up approximant.", 5) - def _computeInterpolantInverseBlocks(self) -> List[Np2D]: + def _computeInterpolantInverseBlocks(self) -> Tuple[List[Np2D], Np2D]: """ Compute inverse factors for minimal interpolant target functional. """ RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.") self._setupInterpolationIndices() while self.E >= 0: eWidth = (hashD([self.E + 1] + [0] * (self.npar - 1)) - hashD([self.E] + [0] * (self.npar - 1))) TE, _, argIdxs = hpvP(self._musUniqueCN, self.E, self.polybasis, self._derIdxs, self._reorder, scl = np.power(self.scaleFactor, -1.)) fitOut = customPInv(TE[:, argIdxs], rcond = self.interpRcond, full = True) - if self.verbosity >= 5: - condfit = fitOut[1][1][0] / fitOut[1][1][-1] - verbosityDepth("MAIN", ("Fitting {} samples with degree {} " - "through {}... Conditioning of " - "pseudoinverse system: {:.4e}.")\ - .format(TE.shape[0], self.E, - polyfitname(self.polybasis), - condfit), - timestamp = self.timestamp) + vbMng(self, "MAIN", + ("Fitting {} samples with degree {} through {}... " + "Conditioning of pseudoinverse system: {:.4e}.").format( + TE.shape[0], self.E, + polyfitname(self.polybasis), + fitOut[1][1][0] / fitOut[1][1][-1]), + 5) if fitOut[1][0] == len(argIdxs): - self._fitinv = fitOut[0][- eWidth : , :] + fitinv = fitOut[0][- eWidth : , :] break RROMPyWarning("Polyfit is poorly conditioned. Reducing E by 1.") self.E -= 1 if self.E < 0: raise RROMPyException(("Instability in computation of " "denominator. Aborting.")) TN, _, argIdxs = hpvP(self._musUniqueCN, self.N, self.polybasis, self._derIdxs, self._reorder, scl = np.power(self.scaleFactor, -1.)) TN = TN[:, argIdxs] invD = [None] * (eWidth) for k in range(eWidth): - pseudoInv = np.diag(self._fitinv[k, :]) + pseudoInv = np.diag(fitinv[k, :]) idxGlob = 0 for j, derIdxs in enumerate(self._derIdxs): nder = len(derIdxs) idxGlob += nder if nder > 1: idxLoc = np.arange(len(self.mus))[ (self._reorder >= idxGlob - nder) * (self._reorder < idxGlob)] - invLoc = self._fitinv[k, idxLoc] + invLoc = fitinv[k, idxLoc] pseudoInv[np.ix_(idxLoc, idxLoc)] = 0. for diffj, diffjIdx in enumerate(derIdxs): for derQ, derQIdx in enumerate(derIdxs): derUIdx = [x - y for (x, y) in zip(diffjIdx, derQIdx)] if all([x >= 0 for x in derUIdx]): derU = hashD(derUIdx) pseudoInv[idxLoc[derU], idxLoc[derQ]] = ( invLoc[diffj]) invD[k] = pseudoInv.dot(TN) - return invD + return invD, fitinv def findeveVGExplicit(self, sampleE:sampList, invD:List[Np2D]) -> Tuple[Np1D, Np2D]: """ - Compute explicitly eigenvalues and eigenvectors of Pade' denominator + Compute explicitly eigenvalues and eigenvectors of rational denominator matrix. """ RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.") nEnd = invD[0].shape[1] eWidth = len(invD) - if self.verbosity >= 10: - verbosityDepth("INIT", "Building gramian matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Building gramian matrix.", 10) gramian = self.HFEngine.innerProduct(sampleE, sampleE) G = np.zeros((nEnd, nEnd), dtype = np.complex) for k in range(eWidth): G += invD[k].T.conj().dot(gramian.dot(invD[k])) - if self.verbosity >= 10: - verbosityDepth("DEL", "Done building gramian.", - timestamp = self.timestamp) - if self.verbosity >= 7: - verbosityDepth("INIT", ("Solving eigenvalue problem for " - "gramian matrix."), - timestamp = self.timestamp) + vbMng(self, "DEL", "Done building gramian.", 10) + vbMng(self, "INIT", "Solving eigenvalue problem for gramian matrix.", + 7) ev, eV = np.linalg.eigh(G) - if self.verbosity >= 5: - try: condev = ev[-1] / ev[0] - except: condev = np.inf - verbosityDepth("MAIN", ("Solved eigenvalue problem of " - "size {} with condition number " - "{:.4e}.").format(nEnd, condev), - timestamp = self.timestamp) - if self.verbosity >= 7: - verbosityDepth("DEL", "Done solving eigenvalue problem.", - timestamp = self.timestamp) + vbMng(self, "MAIN", + ("Solved eigenvalue problem of size {} with condition number " + "{:.4e}.").format(nEnd, ev[-1] / ev[0]), 5) + vbMng(self, "DEL", "Done solving eigenvalue problem.", 7) return ev, eV def findeveVGQR(self, RPODE:Np2D, invD:List[Np2D]) -> Tuple[Np1D, Np2D]: """ - Compute eigenvalues and eigenvectors of Pade' denominator matrix + Compute eigenvalues and eigenvectors of rational denominator matrix through SVD of R factor. """ RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.") nEnd = invD[0].shape[1] S = RPODE.shape[0] eWidth = len(invD) - if self.verbosity >= 10: - verbosityDepth("INIT", "Building half-gramian matrix stack.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Building half-gramian matrix stack.", 10) Rstack = np.zeros((S * eWidth, nEnd), dtype = np.complex) for k in range(eWidth): Rstack[k * S : (k + 1) * S, :] = RPODE.dot(invD[k]) - if self.verbosity >= 10: - verbosityDepth("DEL", "Done building half-gramian.", - timestamp = self.timestamp) - if self.verbosity >= 7: - verbosityDepth("INIT", ("Solving svd for square root of " - "gramian matrix."), - timestamp = self.timestamp) + vbMng(self, "DEL", "Done building half-gramian.", 10) + vbMng(self, "INIT", "Solving svd for square root of gramian matrix.", + 7) _, s, eV = np.linalg.svd(Rstack, full_matrices = False) ev = s[::-1] eV = eV[::-1, :].T.conj() - if self.verbosity >= 5: - try: condev = s[0] / s[-1] - except: condev = np.inf - verbosityDepth("MAIN", ("Solved svd problem of size {} x " - "{} with condition number " - "{:.4e}.").format(*Rstack.shape, condev), - timestamp = self.timestamp) - if self.verbosity >= 7: - verbosityDepth("DEL", "Done solving svd.", - timestamp = self.timestamp) + vbMng(self, "MAIN", + ("Solved svd problem of size {} x {} with condition number " + "{:.4e}.").format(*Rstack.shape, s[0] / s[-1]), 5) + vbMng(self, "DEL", "Done solving svd.", 7) return ev, eV def centerNormalize(self, mu : paramList = [], mu0 : paramVal = None) -> paramList: """ Compute normalized parameter to be plugged into approximant. Args: mu: Parameter(s) 1. mu0: Parameter(s) 2. If None, set to self.mu0. Returns: Normalized parameter. """ return self.trainedModel.centerNormalize(mu, mu0) - def getResidues(self) -> Np1D: + def getResidues(self, *args, **kwargs) -> Np1D: """ Obtain approximant residues. Returns: Matrix with residues as columns. """ - return self.trainedModel.getResidues() + return self.trainedModel.getResidues(*args, **kwargs) diff --git a/rrompy/reduction_methods/distributed/rb_distributed.py b/rrompy/reduction_methods/standard/reduced_basis.py similarity index 72% rename from rrompy/reduction_methods/distributed/rb_distributed.py rename to rrompy/reduction_methods/standard/reduced_basis.py index bc4a326..ac53d48 100644 --- a/rrompy/reduction_methods/distributed/rb_distributed.py +++ b/rrompy/reduction_methods/standard/reduced_basis.py @@ -1,229 +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 . # from copy import deepcopy as copy import numpy as np -from .generic_distributed_approximant import GenericDistributedApproximant -from rrompy.reduction_methods.trained_model import TrainedModelRB as tModel +from .generic_standard_approximant import GenericStandardApproximant +from rrompy.reduction_methods.trained_model import \ + TrainedModelReducedBasis as tModel from rrompy.reduction_methods.trained_model import TrainedModelData -from rrompy.reduction_methods.base.rb_utils import projectAffineDecomposition -from rrompy.utilities.base.types import (Np1D, Np2D, List, Tuple, DictAny, - HFEng, paramVal, sampList) -from rrompy.utilities.base import verbosityDepth +from rrompy.reduction_methods.base.reduced_basis_utils import \ + projectAffineDecomposition +from rrompy.utilities.base.types import (Np1D, Np2D, List, ListAny, Tuple, + DictAny, HFEng, paramVal, sampList) +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.exception_manager import (RROMPyWarning, RROMPyException, RROMPyAssert) -__all__ = ['RBDistributed'] +__all__ = ['ReducedBasis'] -class RBDistributed(GenericDistributedApproximant): +class ReducedBasis(GenericStandardApproximant): """ ROM RB approximant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator; - 'R': rank for Galerkin projection; defaults to prod(S); - 'PODTolerance': tolerance for snapshots POD; defaults to -1. Defaults to empty dict. homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults to False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. homogeneized: Whether to homogeneize Dirichlet BCs. approxRadius: Dummy radius of approximant (i.e. distance from mu0 to farthest sample point). approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. - 'R': rank for Galerkin projection; - 'PODTolerance': tolerance for snapshots POD. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator; POD: Whether to compute POD of snapshots. S: Number of solution snapshots over which current approximant is based upon. sampler: Sample point generator. R: Rank for Galerkin projection. muBounds: list of bounds for parameter values. samplingEngine: Sampling engine. uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as sampleList. lastSolvedHF: Parameter(s) corresponding to last computed high fidelity solution(s) as parameterList. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApprox: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. As: List of sparse matrices (in CSC format) representing coefficients - of linear system matrix wrt theta(mu). + of linear system matrix. 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. + RHS. ARBs: List of sparse matrices (in CSC format) representing coefficients - of compressed linear system matrix wrt theta(mu). + of compressed linear system matrix. bRBs: List of numpy vectors representing coefficients of compressed - linear system RHS wrt theta(mu). + linear system RHS. """ def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["R", "PODTolerance"], [1, -1]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) - if self.verbosity >= 10: - verbosityDepth("INIT", "Computing affine blocks of system.", - timestamp = self.timestamp) - self.As = self.HFEngine.affineLinearSystemA(self.mu0) - self.bs = self.HFEngine.affineLinearSystemb(self.mu0, - self.homogeneized) - if self.verbosity >= 10: - verbosityDepth("DEL", "Done computing affine blocks.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Computing affine blocks of system.", 10) + vbMng(self, "DEL", "Done computing affine blocks.", 10) self._postInit() @property def S(self): """Value of S.""" return self._S @S.setter def S(self, S): - GenericDistributedApproximant.S.fset(self, S) + GenericStandardApproximant.S.fset(self, S) if not hasattr(self, "_R"): self._R = np.prod(self.S) @property def R(self): """Value of R. Its assignment may change S.""" return self._R @R.setter def R(self, R): if R < 0: raise RROMPyException("R must be non-negative.") self._R = R self._approxParameters["R"] = self.R if hasattr(self, "_S") and np.prod(self.S) < self.R: RROMPyWarning("Prescribed S is too small. Decreasing R.") self.R = np.prod(self.S) @property def PODTolerance(self): """Value of PODTolerance.""" return self._PODTolerance @PODTolerance.setter def PODTolerance(self, PODTolerance): self._PODTolerance = PODTolerance self._approxParameters["PODTolerance"] = self.PODTolerance def setupApprox(self): """Compute RB projection matrix.""" if self.checkComputedApprox(): return RROMPyAssert(self._mode, message = "Cannot setup approximant.") - if self.verbosity >= 5: - verbosityDepth("INIT", "Setting up {}.". format(self.name()), - timestamp = self.timestamp) + vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5) + self.computeScaleFactor() self.computeSnapshots() - if self.verbosity >= 7: - verbosityDepth("INIT", "Computing projection matrix.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Computing projection matrix.", 7) if self.POD: U, s, _ = np.linalg.svd(self.samplingEngine.RPOD) s = s ** 2. else: Gramian = self.HFEngine.innerProduct(self.samplingEngine.samples, self.samplingEngine.samples) U, s, _ = np.linalg.svd(Gramian) nsamples = self.samplingEngine.nsamples snorm = np.cumsum(s[::-1]) / np.sum(s) nPODTrunc = min(nsamples - np.argmax(snorm > self.PODTolerance), self.R) pMat = self.samplingEngine.samples.dot(U[:, : nPODTrunc]) - if self.verbosity >= 5: - verbosityDepth("MAIN", ("Assembling {}x{} projection matrix from " - "{} samples.").format(*(pMat.shape), - nsamples), - timestamp = self.timestamp) - + vbMng(self, "MAIN", + ("Assembling {}x{} projection matrix from {} " + "samples.").format(*(pMat.shape), nsamples), 5) if self.trainedModel is None: self.trainedModel = tModel() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp data = TrainedModelData(self.trainedModel.name(), self.mu0, - pMat, self.HFEngine.rescalingExp) - data.thetaAs = self.HFEngine.affineWeightsA(self.mu0) - data.thetabs = self.HFEngine.affineWeightsb(self.mu0, - self.homogeneized) + pMat, self.scaleFactor, + self.HFEngine.rescalingExp) data.mus = copy(self.mus) self.trainedModel.data = data else: self.trainedModel = self.trainedModel self.trainedModel.data.projMat = copy(pMat) + vbMng(self, "INIT", "Computing affine blocks of system.", 10) + self.As = self.HFEngine.affineLinearSystemA(self.mu0, self.scaleFactor) + self.bs = self.HFEngine.affineLinearSystemb(self.mu0, self.scaleFactor, + self.homogeneized) + vbMng(self, "DEL", "Done computing affine blocks.", 10) ARBs, bRBs = self.assembleReducedSystem(pMat) self.trainedModel.data.ARBs = ARBs self.trainedModel.data.bRBs = bRBs - if self.verbosity >= 7: - verbosityDepth("DEL", "Done computing projection matrix.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done computing projection matrix.", 7) self.trainedModel.data.approxParameters = copy(self.approxParameters) - if self.verbosity >= 5: - verbosityDepth("DEL", "Done setting up approximant.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done setting up approximant.", 5) def assembleReducedSystem(self, pMat : sampList = None, pMatOld : sampList = None)\ -> Tuple[List[Np2D], List[Np1D]]: """Build affine blocks of RB linear system through projections.""" if pMat is None: self.setupApprox() ARBs = self.trainedModel.data.ARBs bRBs = self.trainedModel.data.bRBs else: - if self.verbosity >= 10: - verbosityDepth("INIT", "Projecting affine terms of HF model.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Projecting affine terms of HF model.", 10) ARBsOld = None if pMatOld is None else self.trainedModel.data.ARBs bRBsOld = None if pMatOld is None else self.trainedModel.data.bRBs ARBs, bRBs = projectAffineDecomposition(self.As, self.bs, pMat, ARBsOld, bRBsOld, pMatOld) - if self.verbosity >= 10: - verbosityDepth("DEL", "Done projecting affine terms.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done projecting affine terms.", 10) return ARBs, bRBs diff --git a/rrompy/reduction_methods/trained_model/__init__.py b/rrompy/reduction_methods/trained_model/__init__.py index 35dfee6..a80b481 100644 --- a/rrompy/reduction_methods/trained_model/__init__.py +++ b/rrompy/reduction_methods/trained_model/__init__.py @@ -1,31 +1,47 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from .trained_model import TrainedModel from .trained_model_data import TrainedModelData -from .trained_model_pade import TrainedModelPade -from .trained_model_rb import TrainedModelRB +from .trained_model_rational import TrainedModelRational +from .trained_model_reduced_basis import TrainedModelReducedBasis +from .trained_model_pivoted_data import TrainedModelPivotedData +from .trained_model_pivoted_rational import TrainedModelPivotedRational +from .trained_model_pivoted_reduced_basis import \ + TrainedModelPivotedReducedBasis +from .trained_model_pole_matching_general import \ + TrainedModelPoleMatchingGeneral +from .trained_model_pole_matching_rational import \ + TrainedModelPoleMatchingRational +from .trained_model_pole_matching_reduced_basis import \ + TrainedModelPoleMatchingReducedBasis __all__ = [ 'TrainedModel', 'TrainedModelData', - 'TrainedModelPade', - 'TrainedModelRB' + 'TrainedModelRational', + 'TrainedModelReducedBasis', + 'TrainedModelPivotedData', + 'TrainedModelPivotedRational', + 'TrainedModelPivotedReducedBasis', + 'TrainedModelPoleMatchingGeneral', + 'TrainedModelPoleMatchingRational', + 'TrainedModelPoleMatchingReducedBasis' ] diff --git a/rrompy/reduction_methods/trained_model/trained_model.py b/rrompy/reduction_methods/trained_model/trained_model.py index 9a2b190..dfc80ee 100644 --- a/rrompy/reduction_methods/trained_model/trained_model.py +++ b/rrompy/reduction_methods/trained_model/trained_model.py @@ -1,89 +1,89 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from abc import abstractmethod from rrompy.utilities.base.types import Np1D, paramList, sampList from rrompy.parameter import checkParameterList from rrompy.sampling import sampleList, emptySampleList __all__ = ['TrainedModel'] class TrainedModel: """ ABSTRACT ROM approximant evaluation. Attributes: Data: dictionary with all that can be pickled. """ def name(self) -> str: return self.__class__.__name__ def __str__(self) -> str: return self.name() def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) @abstractmethod def getApproxReduced(self, mu : paramList = []) -> sampList: """ Evaluate reduced representation of approximant at arbitrary parameter. (ABSTRACT) Args: mu: Target parameter. """ pass def getApprox(self, mu : paramList = []) -> sampList: """ Evaluate approximant at arbitrary parameter. Args: mu: Target parameter. """ - mu, _ = checkParameterList(mu, self.data.npar) + mu = checkParameterList(mu, self.data.npar)[0] if (not hasattr(self, "lastSolvedApprox") or self.lastSolvedApprox != mu): uApproxR = self.getApproxReduced(mu) self.uApprox = emptySampleList() self.uApprox.reset((self.data.projMat.shape[0], len(mu)), self.data.projMat.dtype) for i in range(len(mu)): if isinstance(self.data.projMat, (list, sampleList,)): self.uApprox[i] = uApproxR[i][0] * self.data.projMat[0] for j in range(1, uApproxR.shape[0]): self.uApprox[i] += (uApproxR[i][j] * self.data.projMat[j]) else: self.uApprox[i] = self.data.projMat.dot(uApproxR[i]) self.lastSolvedApprox = mu return self.uApprox @abstractmethod def getPoles(self) -> Np1D: """ Obtain approximant poles. Returns: Numpy complex vector of poles. """ pass diff --git a/rrompy/reduction_methods/trained_model/trained_model_data.py b/rrompy/reduction_methods/trained_model/trained_model_data.py index 63620a7..766f236 100644 --- a/rrompy/reduction_methods/trained_model/trained_model_data.py +++ b/rrompy/reduction_methods/trained_model/trained_model_data.py @@ -1,35 +1,37 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import deepcopy as copy from rrompy.utilities.base.types import Np2D, List, paramVal from rrompy.utilities.exception_manager import RROMPyAssert __all__ = ['TrainedModelData'] class TrainedModelData: """ROM approximant evaluation data (must be pickle-able).""" def __init__(self, name:str, mu0:paramVal, projMat:Np2D, + scaleFactor : List[float] = [1.], rescalingExp : List[float] = [1.]): self.npar = len(rescalingExp) RROMPyAssert(mu0.shape[1], self.npar, "Number of parameters") self.name = name self.mu0 = mu0 self.projMat = copy(projMat) + self.scaleFactor = scaleFactor self.rescalingExp = rescalingExp diff --git a/rrompy/reduction_methods/trained_model/trained_model_pade.py b/rrompy/reduction_methods/trained_model/trained_model_pade.py deleted file mode 100644 index 86a3d4f..0000000 --- a/rrompy/reduction_methods/trained_model/trained_model_pade.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -import numpy as np -from . import TrainedModel -from rrompy.utilities.base.types import (Np1D, List, paramVal, paramList, - sampList) -from rrompy.utilities.base import verbosityDepth -from rrompy.utilities.poly_fitting.polynomial import polyval, polyroots -from rrompy.utilities.poly_fitting.radial_basis import polyval as polyvalR -from rrompy.utilities.exception_manager import RROMPyAssert -from rrompy.parameter import checkParameterList -from rrompy.sampling import sampleList - -__all__ = ['TrainedModelPade'] - -class TrainedModelPade(TrainedModel): - """ - ROM approximant evaluation for Pade' approximant. - - Attributes: - Data: dictionary with all that can be pickled. - """ - - def centerNormalize(self, mu : paramList = [], - mu0 : paramVal = None) -> paramList: - """ - Compute normalized parameter to be plugged into approximant. - - Args: - mu: Parameter(s) 1. - mu0: Parameter(s) 2. If None, set to self.data.mu0. - - Returns: - Normalized parameter. - """ - mu, _ = checkParameterList(mu, self.data.npar) - if mu0 is None: mu0 = self.data.mu0 - rad = ((mu ** self.data.rescalingExp - mu0 ** self.data.rescalingExp) - / self.data.scaleFactor) - return rad - - def getPVal(self, mu : paramList = [], der : List[int] = None) -> sampList: - """ - Evaluate Pade' numerator at arbitrary parameter. - - Args: - mu: Target parameter. - der(optional): Derivatives to take before evaluation. - """ - mu, _ = checkParameterList(mu, self.data.npar) - if self.verbosity >= 17: - verbosityDepth("INIT", ("Evaluating numerator at mu = " - "{}.").format(mu), - timestamp = self.timestamp) - muCenter = self.centerNormalize(mu) - if "_" in self.data.polytypeP: - p = sampleList(polyvalR(muCenter, self.data.P, - self.data.polytypeP, der)) - else: - p = sampleList(polyval(muCenter, self.data.P, - self.data.polytypeP, der)) - if self.verbosity >= 17: - verbosityDepth("DEL", "Done evaluating numerator.", - timestamp = self.timestamp) - return p - - def getQVal(self, mu:Np1D, der : List[int] = None, - scl : Np1D = None) -> Np1D: - """ - Evaluate Pade' denominator at arbitrary parameter. - - Args: - mu: Target parameter. - der(optional): Derivatives to take before evaluation. - """ - mu, _ = checkParameterList(mu, self.data.npar) - if self.verbosity >= 17: - verbosityDepth("INIT", ("Evaluating denominator at mu = " - "{}.").format(mu), - timestamp = self.timestamp) - muCenter = self.centerNormalize(mu) - q = polyval(muCenter, self.data.Q, self.data.polytype, der, scl) - if self.verbosity >= 17: - verbosityDepth("DEL", "Done evaluating denominator.", - timestamp = self.timestamp) - return q - - def getApproxReduced(self, mu : paramList = []) -> sampList: - """ - Evaluate reduced representation of approximant at arbitrary parameter. - - Args: - mu: Target parameter. - """ - mu, _ = checkParameterList(mu, self.data.npar) - if (not hasattr(self, "lastSolvedApproxReduced") - or self.lastSolvedApproxReduced != mu): - if self.verbosity >= 12: - verbosityDepth("INIT", ("Evaluating approximant at mu = " - "{}.").format(mu), - timestamp = self.timestamp) - self.uApproxReduced = self.getPVal(mu) / self.getQVal(mu) - if self.verbosity >= 12: - verbosityDepth("DEL", "Done evaluating approximant.", - timestamp = self.timestamp) - self.lastSolvedApproxReduced = mu - return self.uApproxReduced - - def getPoles(self) -> Np1D: - """ - Obtain approximant poles. - - Returns: - Numpy complex vector of poles. - """ - RROMPyAssert(self.data.npar, 1, "Number of parameters") - return np.power(self.data.mu0(0) ** self.data.rescalingExp[0] - + self.data.scaleFactor - * polyroots(self.data.Q, self.data.polytype), - 1. / self.data.rescalingExp[0]) - - def getResidues(self) -> Np1D: - """ - Obtain approximant residues. - - Returns: - Numpy matrix with residues as columns. - """ - pls = self.getPoles() - poles, _ = checkParameterList(pls, 1) - res = (self.data.projMat.dot(self.getPVal(poles).data) - / self.getQVal(poles, 1)) - return pls, res - diff --git a/rrompy/reduction_methods/trained_model/trained_model_pivoted_data.py b/rrompy/reduction_methods/trained_model/trained_model_pivoted_data.py new file mode 100644 index 0000000..d8ba156 --- /dev/null +++ b/rrompy/reduction_methods/trained_model/trained_model_pivoted_data.py @@ -0,0 +1,70 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +from .trained_model_data import TrainedModelData +from rrompy.utilities.base.types import Np2D, List, ListAny, paramVal +from rrompy.parameter import checkParameterList + +__all__ = ['TrainedModelPivotedData'] + +class TrainedModelPivotedData(TrainedModelData): + """ROM approximant evaluation data (must be pickle-able).""" + def __init__(self, name:str, mu0:paramVal, projMat:Np2D, + scaleFactor : ListAny = [1.], + rescalingExp : List[float] = [1.], + directionPivot : ListAny = [0]): + super().__init__(name, mu0, projMat, scaleFactor, rescalingExp) + self.directionPivot = directionPivot + + @property + def directionMarginal(self): + return tuple([x for x in range(self.npar) \ + if x not in self.directionPivot]) + + @property + def mu0Pivot(self): + return checkParameterList(self.mu0(0, self.directionPivot))[0] + + @property + def mu0Marginal(self): + return checkParameterList(self.mu0(0, self.directionMarginal))[0] + + @property + def nparPivot(self): + return len(self.directionPivot) + + @property + def nparMarginal(self): + return self.npar - self.nparPivot + + @property + def rescalingExpPivot(self): + return [self.rescalingExp[x] for x in self.directionPivot] + + @property + def rescalingExpMarginal(self): + return [self.rescalingExp[x] for x in self.directionMarginal] + + @property + def scaleFactorPivot(self): + return [self.scaleFactor[x] for x in self.directionPivot] + + @property + def scaleFactorMarginal(self): + return [self.scaleFactor[x] for x in self.directionMarginal] + diff --git a/rrompy/reduction_methods/trained_model/trained_model_pivoted_rational.py b/rrompy/reduction_methods/trained_model/trained_model_pivoted_rational.py new file mode 100644 index 0000000..2417a25 --- /dev/null +++ b/rrompy/reduction_methods/trained_model/trained_model_pivoted_rational.py @@ -0,0 +1,225 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from .trained_model_rational import TrainedModelRational +from rrompy.utilities.base.types import (Np1D, List, ListAny, paramVal, + paramList, sampList) +from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp +from rrompy.utilities.poly_fitting.polynomial import polyroots +from rrompy.utilities.exception_manager import RROMPyException +from rrompy.parameter import checkParameter, checkParameterList +from rrompy.sampling import sampleList, emptySampleList + +__all__ = ['TrainedModelPivotedRational'] + +class TrainedModelPivotedRational(TrainedModelRational): + """ + ROM approximant evaluation for pivoted Rational approximant. + + Attributes: + Data: dictionary with all that can be pickled. + """ + + def centerNormalizePivot(self, mu : paramList = [], + mu0 : paramVal = None) -> paramList: + """ + Compute normalized parameter to be plugged into approximant. + + Args: + mu: Parameter(s) 1. + mu0: Parameter(s) 2. If None, set to self.data.mu0. + + Returns: + Normalized parameter. + """ + mu = checkParameterList(mu, self.data.nparPivot)[0] + if mu0 is None: mu0 = self.data.mu0(self.data.directionPivot) + rad = ((mu ** self.data.rescalingExpPivot + - mu0 ** self.data.rescalingExpPivot) + / self.data.scaleFactorPivot) + return rad + + def centerNormalizeMarginal(self, mu : paramList = [], + mu0 : paramVal = None) -> paramList: + """ + Compute normalized parameter to be plugged into approximant. + + Args: + mu: Parameter(s) 1. + mu0: Parameter(s) 2. If None, set to self.data.mu0. + + Returns: + Normalized parameter. + """ + mu = checkParameterList(mu, self.data.nparMarginal)[0] + if mu0 is None: mu0 = self.data.mu0(self.data.directionMarginal) + rad = ((mu ** self.data.rescalingExpMarginal + - mu0 ** self.data.rescalingExpMarginal) + / self.data.scaleFactorMarginal) + return rad + + def interpolateMarginal(self, mu : paramList = [], + samples : ListAny = [], der : List[int] = None, + scl : Np1D = None) -> sampList: + """ + Evaluate marginal interpolator at arbitrary marginal parameter. + + Args: + mu: Target parameter. + samples: Objects to interpolate. + der(optional): Derivatives to take before evaluation. + """ + mu = checkParameterList(mu, self.data.nparMarginal)[0] + sList = isinstance(samples[0], sampleList) + sEff = [None] * len(samples) + for j in range(len(samples)): + if sList: + sEff[j] = samples[j].data + else: + sEff[j] = samples[j] + vbMng(self, "INIT", "Interpolating marginal at mu = {}.".format(mu), + 95) + muC = self.centerNormalizeMarginal(mu) + p = emptySampleList() + p.reset((len(sEff[0]), len(muC))) + p.data[:] = 0. + if len(sEff[0]) > 0: + for mIj, spj in zip(self.data.marginalInterp, sEff): + p = p + spj.reshape(len(sEff[0]), - 1) * mIj(muC, der, scl) + vbMng(self, "DEL", "Done interpolating marginal.", 95) + if not sList: + p = p.data.flatten() + return p + + def getPValsPivot(self, mu : paramList = []) -> sampList: + """ + Evaluate rational numerators at arbitrary pivot parameter. + + Args: + mu: Target parameter. + """ + mu = checkParameterList(mu, self.data.nparPivot)[0] + vbMng(self, "INIT", "Evaluating numerator at mu = {}.".format(mu), 17) + muC = self.centerNormalizePivot(mu) + pP = [sampleList(P(muC)) for P in self.data.Ps] + vbMng(self, "DEL", "Done evaluating numerator.", 17) + return pP + + def getPVal(self, mu : paramList = []) -> sampList: + """ + Evaluate rational numerator at arbitrary parameter. + + Args: + mu: Target parameter. + """ + mu = checkParameterList(mu, self.data.npar)[0] + muP = checkParameterList(mu.data[:, self.data.directionPivot], + self.data.nparPivot)[0] + muM = checkParameterList(mu.data[:, self.data.directionMarginal], + self.data.nparMarginal)[0] + pP = self.getPValsPivot(muP) + return self.interpolateMarginal(muM, pP) + + def getQValsPivot(self, mu:Np1D, der : List[int] = None, + scl : Np1D = None) -> Np1D: + """ + Evaluate rational denominators at arbitrary pivot parameter. + + Args: + mu: Target parameter. + der(optional): Derivatives to take before evaluation. + """ + mu = checkParameterList(mu, self.data.nparPivot)[0] + vbMng(self, "INIT", "Evaluating denominator at mu = {}.".format(mu), + 17) + muC = self.centerNormalizePivot(mu) + qP = [Q(muC, der, scl).reshape(1, -1) for Q in self.data.Qs] + vbMng(self, "DEL", "Done evaluating denominator.", 17) + return qP + + def getQVal(self, mu:Np1D, der : List[int] = None, + scl : Np1D = None) -> Np1D: + """ + Evaluate rational denominator at arbitrary parameter. + + Args: + mu: Target parameter. + der(optional): Derivatives to take before evaluation. + """ + mu = checkParameterList(mu, self.data.npar)[0] + muP = checkParameterList(mu.data[:, self.data.directionPivot], + self.data.nparPivot)[0] + muM = checkParameterList(mu.data[:, self.data.directionMarginal], + self.data.nparMarginal)[0] + if der is None: + derP, derM = None, None + else: + derP = [der[x] for x in self.data.directionPivot] + derM = [der[x] for x in self.data.directionMarginal] + if scl is None: + sclP, sclM = None, None + else: + sclP = [scl[x] for x in self.data.directionPivot] + sclM = [scl[x] for x in self.data.directionMarginal] + qP = self.getQValsPivot(muP, derP, sclP) + return self.interpolateMarginal(muM, qP, derM, sclM) + + def getPoles(self, *args, **kwargs) -> Np1D: + """ + Obtain approximant poles. + + Returns: + Numpy complex vector of poles. + """ + if len(args) + len(kwargs) > 1: + raise RROMPyException(("Wrong number of parameters passed. " + "Only 1 available.")) + elif len(args) + len(kwargs) == 1: + if len(args) == 1: + mVals = args[0] + else: + mVals = kwargs["marginalVals"] + if not hasattr(mVals, "__len__"): mVals = [mVals] + mVals = list(mVals) + else: + mVals = [fp] + mValsP = [mVals[x] for x in self.data.directionPivot] + mValsM = [mVals[x] for x in self.data.directionMarginal] + try: + rDim = mValsP.index(fp) + if rDim < len(mValsP) - 1 and fp in mValsP[rDim + 1 :]: + raise + except: + raise RROMPyException(("Exactly 1 'freepar' entry in " + "marginalVals must be provided.")) + if fp in mValsM: + raise RROMPyException(("'freepar' entry in marginalVals must be " + "pivot dimension.")) + rDimB = self.data.directionPivot[rDim] + Qeff = self.interpolateMarginal(mValsM, [Q.coeffs[:, np.newaxis] \ + for Q in self.data.Qs]) + Qeff = Qeff.reshape(self.data.Qs[0].coeffs.shape) + mValsP[rDim] = self.data.mu0(rDimB) + mValsP = self.centerNormalizePivot(checkParameter(mValsP)) + mValsP = list(mValsP.data.flatten()) + mValsP[rDim] = fp + return np.power(self.data.mu0(rDimB) ** self.data.rescalingExp[rDimB] + + self.data.scaleFactor[rDimB] + * polyroots(Qeff, self.data.Qs[0].polybasis, mValsP), + 1. / self.data.rescalingExp[rDimB]) diff --git a/rrompy/reduction_methods/trained_model/trained_model_pivoted_reduced_basis.py b/rrompy/reduction_methods/trained_model/trained_model_pivoted_reduced_basis.py new file mode 100644 index 0000000..490a1b0 --- /dev/null +++ b/rrompy/reduction_methods/trained_model/trained_model_pivoted_reduced_basis.py @@ -0,0 +1,202 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from .trained_model_reduced_basis import TrainedModelReducedBasis +from rrompy.utilities.base.types import (Np1D, ListAny, paramVal, paramList, + sampList) +from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp +from rrompy.utilities.poly_fitting.polynomial import ( + hashIdxToDerivative as hashI) +from rrompy.utilities.numerical import (eigvalsNonlinearDense, + marginalizePolyList) +from rrompy.utilities.exception_manager import RROMPyException +from rrompy.parameter import checkParameter, checkParameterList +from rrompy.sampling import emptySampleList, sampleList + +__all__ = ['TrainedModelPivotedReducedBasis'] + +class TrainedModelPivotedReducedBasis(TrainedModelReducedBasis): + """ + ROM approximant evaluation for pivoted RB approximant. + + Attributes: + Data: dictionary with all that can be pickled. + """ + + def centerNormalizePivot(self, mu : paramList = [], + mu0 : paramVal = None) -> paramList: + """ + Compute normalized parameter to be plugged into approximant. + + Args: + mu: Parameter(s) 1. + mu0: Parameter(s) 2. If None, set to self.data.mu0. + + Returns: + Normalized parameter. + """ + mu = checkParameterList(mu, self.data.nparPivot)[0] + if mu0 is None: mu0 = self.data.mu0Pivot + rad = ((mu ** self.data.rescalingExpPivot + - mu0 ** self.data.rescalingExpPivot) + / self.data.scaleFactorPivot) + return rad + + def centerNormalizeMarginal(self, mu : paramList = [], + mu0 : paramVal = None) -> paramList: + """ + Compute normalized parameter to be plugged into approximant. + + Args: + mu: Parameter(s) 1. + mu0: Parameter(s) 2. If None, set to self.data.mu0. + + Returns: + Normalized parameter. + """ + mu = checkParameterList(mu, self.data.nparMarginal)[0] + if mu0 is None: mu0 = self.data.mu0Marginal + rad = ((mu ** self.data.rescalingExpMarginal + - mu0 ** self.data.rescalingExpMarginal) + / self.data.scaleFactorMarginal) + return rad + + def interpolateMarginal(self, mu : paramVal = [], + samples : ListAny = []) -> ListAny: + """ + Evaluate marginal interpolator at arbitrary marginal parameter. + + Args: + mu: Target parameter. + samples: Objects to interpolate. + """ + mu = checkParameter(mu, self.data.nparMarginal) + sList = isinstance(samples[0], sampleList) + sEff = [None] * len(samples) + for j in range(len(samples)): + if sList: + sEff[j] = samples[j].data + else: + sEff[j] = samples[j] + vbMng(self, "INIT", "Interpolating marginal at mu = {}.".format(mu), + 95) + muC = self.centerNormalizeMarginal(mu) + p = np.zeros_like(sEff[0], dtype = sEff[0].dtype) + for mIj, spj in zip(self.data.marginalInterp, sEff): + p += mIj(muC) * spj + vbMng(self, "DEL", "Done interpolating marginal.", 95) +# if sList: p = sampleList(p) + return p + + def getApproxReduced(self, mu : paramList = []) -> sampList: + """ + Evaluate reduced representation of approximant at arbitrary parameter. + + Args: + mu: Target parameter. + """ + mu = checkParameterList(mu, self.data.npar)[0] + if (not hasattr(self, "lastSolvedApproxReduced") + or self.lastSolvedApproxReduced != mu): + vbMng(self, "INIT", + "Computing RB solution at mu = {}.".format(mu), 12) + self.uApproxReduced = emptySampleList() + self.uApproxReduced.reset((self.data.ARBsList[0][0].shape[0], + len(mu)), self.data.projMat[0].dtype) + for i, muPL in enumerate(mu): + muP = checkParameter([muPL(0, x) \ + for x in self.data.directionPivot]) + muM = [muPL(0, x) for x in self.data.directionMarginal] + vbMng(self, "INIT", + "Assembling reduced model for mu = {}.".format(muPL), 87) + ARBs = self.interpolateMarginal(muM, self.data.ARBsList) + bRBs = self.interpolateMarginal(muM, self.data.bRBsList) + ARBmu = ARBs[0] + bRBmu = bRBs[0] + for j in range(1, max(len(ARBs), len(bRBs))): + theta = np.prod(self.centerNormalizePivot(muP) + ** hashI(j, self.data.nparPivot)) + if j < len(ARBs): + ARBmu += theta * ARBs[j] + if j < len(bRBs): + bRBmu += theta * bRBs[j] + vbMng(self, "DEL", "Done assembling reduced model.", 87) + vbMng(self, "INIT", + "Solving reduced model for mu = {}.".format(muPL), 75) + self.uApproxReduced[i] = np.linalg.solve(ARBmu, bRBmu) + vbMng(self, "DEL", "Done solving reduced model.", 75) + vbMng(self, "DEL", "Done computing RB solution.", 12) + self.lastSolvedApproxReduced = mu + return self.uApproxReduced + + def getApprox(self, mu : paramList = []) -> sampList: + """ + Evaluate approximant at arbitrary parameter. + + Args: + mu: Target parameter. + """ + mu = checkParameterList(mu, self.data.npar)[0] + if (not hasattr(self, "lastSolvedApprox") + or self.lastSolvedApprox != mu): + uApproxR = self.getApproxReduced(mu) + self.uApprox = emptySampleList() + self.uApprox.reset((self.data.projMat[0].shape[0], len(mu)), + self.data.projMat[0].dtype) + for i in range(len(mu)): + muM = [mu(i, x) for x in self.data.directionMarginal] + projLoc = self.interpolateMarginal(muM, self.data.projMat)[0] + self.uApprox[i] = projLoc.dot(uApproxR[i]) + self.lastSolvedApprox = mu + return self.uApprox + + def getPoles(self, marginalVals : ListAny = [fp], jSupp : int = 1, + **kwargs) -> Np1D: + """ + Obtain approximant poles. + + Returns: + Numpy complex vector of poles. + """ + if not hasattr(marginalVals, "__len__"): marginalVals = [marginalVals] + marginalVals = list(marginalVals) + try: + rDim = marginalVals.index(fp) + if rDim < len(marginalVals) - 1 and fp in marginalVals[rDim + 1 :]: + raise + except: + raise RROMPyException(("Exactly 1 'freepar' entry in " + "marginalVals must be provided.")) + if rDim in self.data.directionMarginal: + raise RROMPyException(("'freepar' entry in marginalVals must be " + "in pivot dimension.")) + mValsP = [marginalVals[x] for x in self.data.directionPivot] + rDimP = mValsP.index(fp) + mValsM = [marginalVals[x] for x in self.data.directionMarginal] + ARBs = self.interpolateMarginal(mValsM, self.data.ARBsList) + if self.data.nparPivot > 1: + mValsP[rDimP] = self.data.mu0Pivot(rDimP) + mValsP = self.centerNormalizePivot(mValsP) + mValsP = mValsP.data.flatten() + mValsP[rDimP] = fp + ARBs = marginalizePolyList(ARBs, mValsP, "auto") + ev = eigvalsNonlinearDense(ARBs, jSupp = jSupp, **kwargs) + return np.power(self.data.mu0(rDim) ** self.data.rescalingExp[rDim] + + ev * self.scaleFactor[rDim], + 1. / self.data.rescalingExp[rDim]) diff --git a/rrompy/reduction_methods/trained_model/trained_model_pole_matching_general.py b/rrompy/reduction_methods/trained_model/trained_model_pole_matching_general.py new file mode 100644 index 0000000..3f088c5 --- /dev/null +++ b/rrompy/reduction_methods/trained_model/trained_model_pole_matching_general.py @@ -0,0 +1,244 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from .trained_model import TrainedModel +from rrompy.utilities.base.types import (Np1D, Tuple, ListAny, paramVal, + paramList, sampList, HFEng) +from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp +from rrompy.utilities.numerical import pointMatching +from rrompy.utilities.poly_fitting.heaviside import HeavisideInterpolator as HI +from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert +from rrompy.parameter import checkParameter, checkParameterList +from rrompy.sampling import emptySampleList + +__all__ = ['TrainedModelPoleMatchingGeneral'] + +class TrainedModelPoleMatchingGeneral(TrainedModel): + """ + ROM approximant evaluation for pivoted approximants with pole matching. + + Attributes: + Data: dictionary with all that can be pickled. + """ + + def initializeFromLists(self, poles:ListAny, coeffs:ListAny, basis:str, + HFEngine:HFEng, matchingWeight : float = 1., + POD : bool = True): + """Initialize Heaviside representation.""" + musM = self.data.musMarginal + margAbsDist = np.sum(np.abs(np.tile(musM.data.reshape(len(musM),1,-1), + [1, len(musM), 1]) + - np.tile(musM.data.reshape(1,len(musM),-1), + [len(musM), 1, 1])), axis = 2) + N = len(poles[0]) + explored = [0] + unexplored = list(range(1, len(musM))) + for _ in range(1, len(musM)): + minIdx = np.argmin(np.concatenate([margAbsDist[ex, unexplored] \ + for ex in explored])) + minIex = explored[minIdx // len(unexplored)] + minIunex = unexplored[minIdx % len(unexplored)] + dist = np.abs(np.tile(poles[minIex].reshape(-1, 1), N) + - poles[minIunex].reshape(1, -1)) + if matchingWeight != 0: + resex = coeffs[minIex][: N] + resunex = coeffs[minIunex][: N] + if POD: + distR = resex.dot(resunex.T.conj()) + distR = (distR.T / np.linalg.norm(resex, axis = 1)).T + distR = distR / np.linalg.norm(resunex, axis = 1) + else: + resex = self.data.projMat.dot(resex.T) + resunex = self.data.projMat.dot(resunex.T) + distR = HFEngine.innerProduct(resex, resunex).T + distR = (distR.T / HFEngine.norm(resex)).T + distR = distR / HFEngine.norm(resunex) + distR = np.abs(distR) + distR[distR > 1.] = 1. + dist += 2. / np.pi * matchingWeight * np.arccos(distR) + reordering = pointMatching(dist, verbObj = self) + poles[minIunex] = poles[minIunex][reordering] + coeffs[minIunex][: N] = coeffs[minIunex][reordering] + explored += [minIunex] + unexplored.remove(minIunex) +# explored = np.append(explored, minIunex) +# unexplored = unexplored[unexplored != minIunex] + HIs = [] + for pls, cfs in zip(poles, coeffs): + hsi = HI() + hsi.poles = pls + hsi.coeffs = cfs + hsi.npar = 1 + hsi.polybasis = basis + HIs += [hsi] + self.data.HIs = HIs + + def recompressByCutOff(self, murange : Tuple[float, float] = [- 1., 1.], + tol : float = np.inf, rtype : str = "MAGNITUDE"): + if np.isinf(tol): return " No poles erased." + N = len(self.data.HIs[0].poles) + mu0 = np.mean(murange) + musig = murange[0] - mu0 + if np.isclose(musig, 0.): + radius = lambda x: np.abs(x - mu0) + else: + if rtype == "MAGNITUDE": + murdir = (murange[0] - mu0) / np.abs(musig) + def radius(x): + scalprod = (x - mu0) * murdir.conj() / np.abs(musig) + rescalepar = np.abs(np.real(scalprod)) + rescaleort = np.abs(np.imag(scalprod)) + return ((rescalepar - 1.) ** 2. * (rescalepar > 1.) + + rescaleort ** 2.) ** .5 + else:#if rtype == "POTENTIAL": + def radius(x): + rescale = (x - mu0) / musig + return np.max(np.abs(rescale * np.array([-1., 1.]) + + (rescale ** 2. - 1) ** .5)) - 1. + keepPole, removePole = [], [] + for j in range(N): + for hi in self.data.HIs: + if radius(hi.poles[j]) <= tol: + keepPole += [j] + break + if len(keepPole) == 0 or keepPole[-1] != j: removePole += [j] + if len(keepPole) == N: return " No poles erased." + keepCoeff = keepPole + [N] + keepCoeff = keepCoeff + list(range(N + 1,len(self.data.HIs[0].coeffs))) + for hi in self.data.HIs: + polyCorrection = np.zeros_like(hi.coeffs[0, :]) + for j in removePole: + polyCorrection += hi.coeffs[j, :] / (mu0 - hi.poles[j]) + if len(hi.coeffs) == N: + hi.coeffs = np.vstack((hi.coeffs, polyCorrection)) + else: + hi.coeffs[N, :] += polyCorrection + hi.poles = hi.poles[keepPole] + hi.coeffs = hi.coeffs[keepCoeff, :] + return (" Erased {} pole".format(len(removePole)) + + "s" * (len(removePole) > 1) + ".") + + def interpolateMarginalInterpolator(self, mu : paramVal = []) -> Np1D: + """Obtain interpolated approximant interpolator.""" + mu = checkParameter(mu, self.data.nparMarginal)[0] + hsi = HI() + hsi.poles = self.interpolateMarginalPoles(mu) + hsi.coeffs = self.interpolateMarginalCoeffs(mu) + hsi.npar = 1 + hsi.polybasis = self.data.HIs[0].polybasis + return hsi + + def interpolateMarginalPoles(self, mu : paramList = []) -> Np1D: + """Obtain interpolated approximant poles.""" + mu = checkParameterList(mu, self.data.nparMarginal)[0] + return self.interpolateMarginal(mu, [hi.poles for hi in self.data.HIs]) + + def interpolateMarginalCoeffs(self, mu : paramList = []) -> Np1D: + """Obtain interpolated approximant coefficients.""" + mu = checkParameterList(mu, self.data.nparMarginal)[0] + cs = self.interpolateMarginal(mu, [hi.coeffs for hi in self.data.HIs]) + if isinstance(cs, (list, tuple,)): cs = np.array(cs) + return cs.reshape(self.data.HIs[0].coeffs.shape) + + def getApproxReduced(self, mu : paramList = []) -> sampList: + """ + Evaluate reduced representation of approximant at arbitrary parameter. + + Args: + mu: Target parameter. + """ + RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") + mu = checkParameterList(mu, self.data.npar)[0] + if (not hasattr(self, "lastSolvedApproxReduced") + or self.lastSolvedApproxReduced != mu): + vbMng(self, "INIT", + "Evaluating approximant at mu = {}.".format(mu), 12) + self.uApproxReduced = emptySampleList() + self.uApproxReduced.reset((self.data.HIs[0].coeffs.shape[1], + len(mu)), self.data.projMat[0].dtype) + + for i, muPL in enumerate(mu): + muL = self.centerNormalizePivot([muPL(0, x) \ + for x in self.data.directionPivot]) + muM = [muPL(0, x) for x in self.data.directionMarginal] + vbMng(self, "INIT", + "Assembling reduced model for mu = {}.".format(muPL), 87) + hsL = self.interpolateMarginalInterpolator(muM) + vbMng(self, "DEL", "Done assembling reduced model.", 87) + self.uApproxReduced[i] = hsL(muL) + vbMng(self, "DEL", "Done evaluating approximant.", 12) + self.lastSolvedApproxReduced = mu + return self.uApproxReduced + + def getPoles(self, *args, **kwargs) -> Np1D: + """ + Obtain approximant poles. + + Returns: + Numpy complex vector of poles. + """ + RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") + if len(args) + len(kwargs) > 1: + raise RROMPyException(("Wrong number of parameters passed. " + "Only 1 available.")) + elif len(args) + len(kwargs) == 1: + if len(args) == 1: + mVals = args[0] + else: + mVals = kwargs["marginalVals"] + if not hasattr(mVals, "__len__"): mVals = [mVals] + mVals = list(mVals) + else: + mVals = [fp] + try: + rDim = mVals.index(fp) + if rDim < len(mVals) - 1 and fp in mVals[rDim + 1 :]: + raise + except: + raise RROMPyException(("Exactly 1 'freepar' entry in " + "marginalVals must be provided.")) + if rDim != self.data.directionPivot[0]: + raise RROMPyException(("'freepar' entry in marginalVals must " + "coincide with pivot direction.")) + mVals[rDim] = self.data.mu0(rDim) + mMarg = [mVals[j] for j in range(len(mVals)) if j != rDim] + roots = np.array(self.interpolateMarginalPoles(mMarg)) + return np.power(self.data.mu0(rDim) ** self.data.rescalingExp[rDim] + + self.data.scaleFactor[rDim] * roots, + 1. / self.data.rescalingExp[rDim]) + + def getResidues(self, *args, **kwargs) -> Np1D: + """ + Obtain approximant residues. + + Returns: + Numpy matrix with residues as columns. + """ + pls = self.getPoles(*args, **kwargs) + if len(args) == 1: + mVals = args[0] + else: + mVals = kwargs["marginalVals"] + if not hasattr(mVals, "__len__"): mVals = [mVals] + mVals = list(mVals) + rDim = mVals.index(fp) + mMarg = [mVals[j] for j in range(len(mVals)) if j != rDim] + residues = self.interpolateMarginalCoeffs(mMarg)[: len(pls)] + res = self.data.projMat.dot(residues) + return pls, res diff --git a/rrompy/reduction_methods/trained_model/trained_model_pole_matching_rational.py b/rrompy/reduction_methods/trained_model/trained_model_pole_matching_rational.py new file mode 100644 index 0000000..c24171f --- /dev/null +++ b/rrompy/reduction_methods/trained_model/trained_model_pole_matching_rational.py @@ -0,0 +1,109 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from scipy.special import factorial as fact +from itertools import combinations +from .trained_model_pole_matching_general import \ + TrainedModelPoleMatchingGeneral +from .trained_model_pivoted_rational import TrainedModelPivotedRational +from rrompy.utilities.base.types import Np1D, List, paramList, sampList, HFEng +from rrompy.utilities.poly_fitting.heaviside import rational2heaviside +from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert +from rrompy.parameter import checkParameterList +from rrompy.sampling import emptySampleList + +__all__ = ['TrainedModelPoleMatchingRational'] + +class TrainedModelPoleMatchingRational(TrainedModelPoleMatchingGeneral, + TrainedModelPivotedRational): + """ + ROM approximant evaluation for pivoted Rational approximant with pole + matching. + + Attributes: + Data: dictionary with all that can be pickled. + """ + + def initializeFromRational(self, HFEngine:HFEng, + matchingWeight : float = 1., POD : bool = True): + """Initialize Heaviside representation.""" + RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") + poles, coeffs = [], [] + for Q, P in zip(self.data.Qs, self.data.Ps): + cfs, pls, basis = rational2heaviside(P, Q) + poles += [pls] + coeffs += [cfs] + self.initializeFromLists(poles, coeffs, basis, HFEngine, + matchingWeight, POD) + self.data._temporary = False + + def getPVal(self, mu : paramList = []) -> sampList: + """ + Evaluate rational numerator at arbitrary parameter. + + Args: + mu: Target parameter. + """ + if self.data._temporary: return super().getPVal(mu) + RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") + mu = checkParameterList(mu, self.data.npar)[0] + p = emptySampleList() + p.reset((len(self.data.HIs[0].coeffs.shape[1]), len(mu))) + for i, muPL in enumerate(mu): + muL = self.centerNormalizePivot([muPL(0, x) \ + for x in self.data.directionPivot]) + muM = [muPL(0, x) for x in self.data.directionMarginal] + hsL = self.interpolateMarginalInterpolator(muM) + p[i] = hsL(muL) * np.prod(muL(0, 0) - hsL.poles) + return p + + def getQVal(self, mu:Np1D, der : List[int] = None, + scl : Np1D = None) -> Np1D: + """ + Evaluate rational denominator at arbitrary parameter. + + Args: + mu: Target parameter. + der(optional): Derivatives to take before evaluation. + """ + if self.data._temporary: return super().getQVal(mu, der, scl) + RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") + mu = checkParameterList(mu, self.data.npar)[0] + muP = self.centerNormalizePivot(checkParameterList( + mu.data[:, self.data.directionPivot], 1)[0]) + muM = checkParameterList(mu.data[:, self.data.directionMarginal], + self.data.npar - 1)[0] + if der is None: + derP, derM = 0, [0] + else: + derP = der[self.data.directionPivot[0]] + derM = [der[x] for x in self.data.directionMarginal] + if np.any(np.array(derM) != 0): + raise RROMPyException(("Derivatives of Q with respect to marginal " + "parameters not allowed.")) + sclP = 1 if scl is None else scl[self.data.directionPivot[0]] + derVal = np.zeros(len(mu), dtype = self.data.HIs[0].poles.dtype) + N = len(self.data.HIs[0].poles) + if derP == N: derVal[:] = 1. + elif derP >= 0 and derP < N: + pls = self.interpolateMarginalPoles(muM).reshape(-1, len(mu)).T + plsDist = muP.data.reshape(-1, 1) - pls + for terms in combinations(np.arange(N), N - derP): + derVal += np.prod(plsDist[:, list(terms)], axis = 1) + return sclP ** derP * fact(derP) * derVal diff --git a/rrompy/reduction_methods/trained_model/trained_model_pole_matching_reduced_basis.py b/rrompy/reduction_methods/trained_model/trained_model_pole_matching_reduced_basis.py new file mode 100644 index 0000000..23802b1 --- /dev/null +++ b/rrompy/reduction_methods/trained_model/trained_model_pole_matching_reduced_basis.py @@ -0,0 +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 .trained_model_pole_matching_general import \ + TrainedModelPoleMatchingGeneral +from .trained_model_pivoted_reduced_basis import \ + TrainedModelPivotedReducedBasis +from rrompy.utilities.base.types import HFEng +from rrompy.utilities.poly_fitting.heaviside import affine2heaviside +from rrompy.utilities.exception_manager import RROMPyAssert + +__all__ = ['TrainedModelPoleMatchingReducedBasis'] + +class TrainedModelPoleMatchingReducedBasis(TrainedModelPoleMatchingGeneral, + TrainedModelPivotedReducedBasis): + """ + ROM approximant evaluation for pivoted RB approximant with pole matching. + + Attributes: + Data: dictionary with all that can be pickled. + """ + + def initializeFromAffine(self, HFEngine:HFEng, matchingWeight : float = 1., + POD : bool = True, jSupp : int = 1): + """Initialize Heaviside representation.""" + RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") + poles, coeffs = [], [] + nbadpls = 0 + for As, bs in zip(self.data.ARBsList, self.data.bRBsList): + cfs, pls, basis = affine2heaviside(As, bs, jSupp) + poles += [pls] + coeffs += [cfs] + nbadpls = max(nbadpls, np.sum(np.isinf(pls))) + if nbadpls > 0: + for j in range(len(poles)): + plsgood = np.argsort(np.abs(pls))[: - nbadpls] + poles[j] = poles[j][plsgood] + coeffs[j] = coeffs[j][plsgood, :] + self.initializeFromLists(poles, coeffs, basis, HFEngine, + matchingWeight, POD) diff --git a/rrompy/reduction_methods/trained_model/trained_model_rational.py b/rrompy/reduction_methods/trained_model/trained_model_rational.py new file mode 100644 index 0000000..8f5ec51 --- /dev/null +++ b/rrompy/reduction_methods/trained_model/trained_model_rational.py @@ -0,0 +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 +from . import TrainedModel +from rrompy.utilities.base.types import (Np1D, List, paramVal, paramList, + sampList) +from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp +from rrompy.utilities.exception_manager import RROMPyException +from rrompy.parameter import (checkParameter, checkParameterList, + emptyParameterList) +from rrompy.sampling import sampleList + +__all__ = ['TrainedModelRational'] + +class TrainedModelRational(TrainedModel): + """ + ROM approximant evaluation for rational approximant. + + Attributes: + Data: dictionary with all that can be pickled. + """ + + def centerNormalize(self, mu : paramList = [], + mu0 : paramVal = None) -> paramList: + """ + Compute normalized parameter to be plugged into approximant. + + Args: + mu: Parameter(s) 1. + mu0: Parameter(s) 2. If None, set to self.data.mu0. + + Returns: + Normalized parameter. + """ + mu = checkParameterList(mu, self.data.npar)[0] + if mu0 is None: mu0 = self.data.mu0 + rad = ((mu ** self.data.rescalingExp - mu0 ** self.data.rescalingExp) + / self.data.scaleFactor) + return rad + + def getPVal(self, mu : paramList = []) -> sampList: + """ + Evaluate rational numerator at arbitrary parameter. + + Args: + mu: Target parameter. + """ + mu = checkParameterList(mu, self.data.npar)[0] + vbMng(self, "INIT", "Evaluating numerator at mu = {}.".format(mu), 17) + muCenter = self.centerNormalize(mu) + p = sampleList(self.data.P(muCenter)) + vbMng(self, "DEL", "Done evaluating numerator.", 17) + return p + + def getQVal(self, mu:Np1D, der : List[int] = None, + scl : Np1D = None) -> Np1D: + """ + Evaluate rational denominator at arbitrary parameter. + + Args: + mu: Target parameter. + der(optional): Derivatives to take before evaluation. + """ + mu = checkParameterList(mu, self.data.npar)[0] + vbMng(self, "INIT", "Evaluating denominator at mu = {}.".format(mu), + 17) + muCenter = self.centerNormalize(mu) + q = self.data.Q(muCenter, der, scl) + vbMng(self, "DEL", "Done evaluating denominator.", 17) + return q + + def getApproxReduced(self, mu : paramList = []) -> sampList: + """ + Evaluate reduced representation of approximant at arbitrary parameter. + + Args: + mu: Target parameter. + """ + mu = checkParameterList(mu, self.data.npar)[0] + if (not hasattr(self, "lastSolvedApproxReduced") + or self.lastSolvedApproxReduced != mu): + vbMng(self, "INIT", + "Evaluating approximant at mu = {}.".format(mu), 12) + self.uApproxReduced = self.getPVal(mu) / self.getQVal(mu) + vbMng(self, "DEL", "Done evaluating approximant.", 12) + self.lastSolvedApproxReduced = mu + return self.uApproxReduced + + def getPoles(self, *args, **kwargs) -> Np1D: + """ + Obtain approximant poles. + + Returns: + Numpy complex vector of poles. + """ + if len(args) + len(kwargs) > 1: + raise RROMPyException(("Wrong number of parameters passed. " + "Only 1 available.")) + elif len(args) + len(kwargs) == 1: + if len(args) == 1: + mVals = args[0] + else: + mVals = kwargs["marginalVals"] + if not hasattr(mVals, "__len__"): mVals = [mVals] + mVals = list(mVals) + else: + mVals = [fp] + try: + rDim = mVals.index(fp) + if rDim < len(mVals) - 1 and fp in mVals[rDim + 1 :]: + raise + except: + raise RROMPyException(("Exactly 1 'freepar' entry in " + "marginalVals must be provided.")) + mVals[rDim] = self.data.mu0(rDim) + mVals = self.centerNormalize(checkParameter(mVals, len(mVals))) + mVals = list(mVals.data.flatten()) + mVals[rDim] = fp + return np.power(self.data.mu0(rDim) ** self.data.rescalingExp[rDim] + + self.data.scaleFactor[rDim] * self.data.Q.roots(mVals), + 1. / self.data.rescalingExp[rDim]) + + def getResidues(self, *args, **kwargs) -> Np1D: + """ + Obtain approximant residues. + + Returns: + Numpy matrix with residues as columns. + """ + pls = self.getPoles(*args, **kwargs) + if len(args) == 1: + mVals = args[0] + else: + mVals = kwargs["marginalVals"] + if not hasattr(mVals, "__len__"): mVals = [mVals] + mVals = list(mVals) + rDim = mVals.index(fp) + poles = emptyParameterList() + poles.reset((len(pls), self.data.npar), dtype = pls.dtype) + for k, pl in enumerate(pls): + poles[k] = mVals + poles.data[k, rDim] = pl + res = (self.data.projMat.dot(self.getPVal(poles).data) + / self.getQVal(poles, + list(1 * (np.arange(self.data.npar) == rDim)))) + return pls, res + diff --git a/rrompy/reduction_methods/trained_model/trained_model_rb.py b/rrompy/reduction_methods/trained_model/trained_model_rb.py deleted file mode 100644 index 9a7ae0b..0000000 --- a/rrompy/reduction_methods/trained_model/trained_model_rb.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -import numpy as np -from scipy.linalg import eigvals -from .trained_model import TrainedModel -from rrompy.utilities.base.types import Np1D, paramList, sampList -from rrompy.utilities.base import verbosityDepth -from rrompy.utilities.exception_manager import RROMPyWarning, RROMPyAssert -from rrompy.parameter import checkParameterList -from rrompy.sampling import emptySampleList - -__all__ = ['TrainedModelRB'] - -class TrainedModelRB(TrainedModel): - """ - ROM approximant evaluation for RB approximant. - - Attributes: - Data: dictionary with all that can be pickled. - """ - - def getApproxReduced(self, mu : paramList = []) -> sampList: - """ - Evaluate reduced representation of approximant at arbitrary parameter. - - Args: - mu: Target parameter. - """ - mus, _ = checkParameterList(mu, self.data.npar) - if (not hasattr(self, "lastSolvedApproxReduced") - or self.lastSolvedApproxReduced != mus): - if self.verbosity >= 12: - verbosityDepth("INIT", ("Computing RB solution at mu = " - "{}.").format(mus), - timestamp = self.timestamp) - thetaAs, thetabs = self.data.thetaAs, self.data.thetabs - ARBs, bRBs = self.data.ARBs, self.data.bRBs - self.uApproxReduced = emptySampleList() - self.uApproxReduced.reset((ARBs[0].shape[0], len(mu)), - self.data.projMat.dtype) - for i, muPL in enumerate(mus): - mu = muPL[0] - if self.verbosity >= 17: - verbosityDepth("INIT", ("Assembling reduced model for mu " - "= {}.").format(mu), - timestamp = self.timestamp) - ARBmu = eval(thetaAs[0]) * ARBs[0] - bRBmu = eval(thetabs[0]) * bRBs[0] - for j in range(1, len(ARBs)): - ARBmu += eval(thetaAs[j]) * ARBs[j] - for j in range(1, len(bRBs)): - bRBmu += eval(thetabs[j]) * bRBs[j] - if self.verbosity >= 17: - verbosityDepth("DEL", "Done assembling reduced model.", - timestamp = self.timestamp) - if self.verbosity >= 15: - verbosityDepth("INIT", ("Solving reduced model for mu = " - "{}.").format(mu), - timestamp = self.timestamp) - self.uApproxReduced[i] = np.linalg.solve(ARBmu, bRBmu) - if self.verbosity >= 15: - verbosityDepth("DEL", "Done solving reduced model.", - timestamp = self.timestamp) - if self.verbosity >= 12: - verbosityDepth("DEL", "Done computing RB solution.", - timestamp = self.timestamp) - self.lastSolvedApproxReduced = mus - return self.uApproxReduced - - def getPoles(self) -> Np1D: - """ - Obtain approximant poles. - - Returns: - Numpy complex vector of poles. - """ - RROMPyAssert(self.data.npar, 1, "Number of parameters") - RROMPyWarning(("Impossible to compute poles in general affine " - "parameter dependence. Results subject to " - "interpretation/rescaling, or possibly completely " - "wrong.")) - ARBs = self.data.ARBs - R = ARBs[0].shape[0] - if len(ARBs) < 2: - return - A = np.eye(R * (len(ARBs) - 1), dtype = np.complex) - B = np.zeros_like(A) - A[: R, : R] = - ARBs[0] - for j in range(len(ARBs) - 1): - Aj = ARBs[j + 1] - B[: R, j * R : (j + 1) * R] = Aj - II = np.arange(R, R * (len(ARBs) - 1)) - B[II, II - R] = 1. - return np.power(eigvals(A, B) - + self.data.mu0(0, 0) ** self.data.rescalingExp[0], - 1. / self.data.rescalingExp[0]) - diff --git a/rrompy/reduction_methods/trained_model/trained_model_reduced_basis.py b/rrompy/reduction_methods/trained_model/trained_model_reduced_basis.py new file mode 100644 index 0000000..6754e66 --- /dev/null +++ b/rrompy/reduction_methods/trained_model/trained_model_reduced_basis.py @@ -0,0 +1,108 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from .trained_model import TrainedModel +from rrompy.utilities.base.types import Np1D, ListAny, paramList, sampList +from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp +from rrompy.utilities.poly_fitting.polynomial import ( + hashIdxToDerivative as hashI) +from rrompy.utilities.numerical import (eigvalsNonlinearDense, + marginalizePolyList) +from rrompy.utilities.exception_manager import RROMPyException +from rrompy.parameter import checkParameter, checkParameterList +from rrompy.sampling import emptySampleList + +__all__ = ['TrainedModelReducedBasis'] + +class TrainedModelReducedBasis(TrainedModel): + """ + ROM approximant evaluation for RB approximant. + + Attributes: + Data: dictionary with all that can be pickled. + """ + + def getApproxReduced(self, mu : paramList = []) -> sampList: + """ + Evaluate reduced representation of approximant at arbitrary parameter. + + Args: + mu: Target parameter. + """ + mu = checkParameterList(mu, self.data.npar)[0] + if (not hasattr(self, "lastSolvedApproxReduced") + or self.lastSolvedApproxReduced != mu): + vbMng(self, "INIT", + "Computing RB solution at mu = {}.".format(mu), 12) + ARBs, bRBs = self.data.ARBs, self.data.bRBs + self.uApproxReduced = emptySampleList() + self.uApproxReduced.reset((ARBs[0].shape[0], len(mu)), + self.data.projMat.dtype) + mu0Eff = (self.data.mu0 ** self.data.rescalingExp).data + for i in range(len(mu)): + vbMng(self, "INIT", "Assembling reduced model for mu = {}."\ + .format(mu[i]), 17) + ARBmu = 1. * ARBs[0] + bRBmu = 1. * bRBs[0] + for j in range(1, max(len(ARBs), len(bRBs))): + derIdx = hashI(j, self.data.npar) + theta = np.prod(((mu[i] ** self.data.rescalingExp - mu0Eff) + / self.data.scaleFactor) ** derIdx) + if j < len(ARBs): + ARBmu += theta * ARBs[j] + if j < len(bRBs): + bRBmu += theta * bRBs[j] + vbMng(self, "DEL", "Done assembling reduced model.", 17) + vbMng(self, "INIT", + "Solving reduced model for mu = {}.".format(mu[i]), 15) + self.uApproxReduced[i] = np.linalg.solve(ARBmu, bRBmu) + vbMng(self, "DEL", "Done solving reduced model.", 15) + vbMng(self, "DEL", "Done computing RB solution.", 12) + self.lastSolvedApproxReduced = mu + return self.uApproxReduced + + def getPoles(self, marginalVals : ListAny = [fp], jSupp : int = 1, + **kwargs) -> Np1D: + """ + Obtain approximant poles. + + Returns: + Numpy complex vector of poles. + """ + if not hasattr(marginalVals, "__len__"): marginalVals = [marginalVals] + mVals = list(marginalVals) + try: + rDim = mVals.index(fp) + if rDim < len(mVals) - 1 and fp in mVals[rDim + 1 :]: + raise + except: + raise RROMPyException(("Exactly 1 'freepar' entry in " + "marginalVals must be provided.")) + ARBs = self.data.ARBs + if self.data.npar > 1: + mVals[rDim] = self.data.mu0(rDim) + mVals = (checkParameter(mVals) ** self.data.rescalingExp + - self.data.mu0 ** self.data.rescalingExp) + mVals = mVals.data.flatten() + mVals[rDim] = fp + ARBs = marginalizePolyList(ARBs, mVals, "auto") + ev = eigvalsNonlinearDense(ARBs, jSupp = jSupp, **kwargs) + return np.power(self.data.mu0(rDim) ** self.data.rescalingExp[rDim] + + ev * self.data.scaleFactor[rDim], + 1. / self.data.rescalingExp[rDim]) diff --git a/rrompy/sampling/base/__init__.py b/rrompy/sampling/base/__init__.py index 09e673f..643e6c4 100644 --- a/rrompy/sampling/base/__init__.py +++ b/rrompy/sampling/base/__init__.py @@ -1,27 +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 .pod_engine import PODEngine from .sampling_engine_base import SamplingEngineBase +from .sampling_engine_base_pivoted import SamplingEngineBasePivoted __all__ = [ 'PODEngine', - 'SamplingEngineBase' + 'SamplingEngineBase', + 'SamplingEngineBasePivoted' ] diff --git a/rrompy/sampling/base/pod_engine.py b/rrompy/sampling/base/pod_engine.py index 82a8699..316a70e 100644 --- a/rrompy/sampling/base/pod_engine.py +++ b/rrompy/sampling/base/pod_engine.py @@ -1,124 +1,133 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from copy import deepcopy as copy from rrompy.utilities.base.types import Np1D, Np2D, Tuple, HFEng, sampList from rrompy.sampling import sampleList __all__ = ['PODEngine'] class PODEngine: """ POD engine for general matrix orthogonalization. """ def __init__(self, HFEngine:HFEng): self.HFEngine = HFEngine def name(self) -> str: return self.__class__.__name__ def __str__(self) -> str: return self.name() def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) def GS(self, a:Np1D, Q:sampList, n : int = -1) -> Tuple[Np1D, Np1D, bool]: """ Compute 1 Gram-Schmidt step with given projector. Args: a: vector to be projected; Q: orthogonal projection matrix; n: number of columns of Q to be considered; Returns: Resulting normalized vector, coefficients of a wrt the updated basis, whether computation is ill-conditioned. """ if n == -1: n = Q.shape[1] r = np.zeros((n + 1,), dtype = Q.dtype) if n > 0: Q = Q[: n] for j in range(2): # twice is enough! nu = self.HFEngine.innerProduct(a, Q) a = a - Q.dot(nu) r[:-1] = r[:-1] + nu.flatten() r[-1] = self.HFEngine.norm(a) ill_cond = False if np.isclose(np.abs(r[-1]) / np.linalg.norm(r), 0.): ill_cond = True r[-1] = 1. a = a / r[-1] return a, r, ill_cond def generalizedQR(self, A:sampList, Q0 : sampList = None, - only_R : bool = False) -> Tuple[sampList, Np2D]: + only_R : bool = False, + genTrials : int = 10) -> Tuple[sampList, Np2D]: """ Compute generalized QR decomposition of a matrix through Householder method. Args: A: matrix to be decomposed; Q0(optional): initial orthogonal guess for Q; defaults to random; only_R(optional): whether to skip reconstruction of Q; defaults to False. + genTrials(optional): number of trials of generation of linearly + independent vector; defaults to 10. Returns: Resulting (orthogonal and )upper-triangular factor(s). """ Nh, N = A.shape B = copy(A) V = copy(A) R = np.zeros((N, N), dtype = A.dtype) if Q0 is None: Q = sampleList(np.zeros(A.shape, dtype = A.dtype) + np.random.randn(*(A.shape))) else: Q = copy(Q0) for k in range(N): - if k <= Nh: - if Q0 is None: - illC = True - while illC: - Q[k], _, illC = self.GS(np.random.randn(Nh), Q, k) - else: - Q[k] = np.zeros(Nh, dtype = Q.dtype) a = B[k] R[k, k] = self.HFEngine.norm(a) - alpha = self.HFEngine.innerProduct(a, Q[k]) - if np.isclose(np.abs(alpha), 0.): s = 1. - else: s = - alpha / np.abs(alpha) - Q[k] = s * Q[k] + if Q0 is None: + for _ in range(genTrials): + Q[k], _, illC = self.GS(np.random.randn(Nh), Q, k) + if not illC: break + else: + illC = False + if illC: + Q[k] = np.zeros(Nh, dtype = Q.dtype) + alpha = 0. + else: + alpha = self.HFEngine.innerProduct(a, Q[k]) + if np.isclose(np.abs(alpha), 0.): s = 1. + else: s = - alpha / np.abs(alpha) + Q[k] = s * Q[k] V[k], _, _ = self.GS(R[k, k] * Q[k] - a, Q, k) J = np.arange(k + 1, N) vtB = self.HFEngine.innerProduct(B[J], V[k]) B.data[:, J] -= 2 * np.outer(V[k], vtB) - R[k, J] = self.HFEngine.innerProduct(B[J], Q[k]) - B.data[:, J] -= np.outer(Q[k], R[k, J]) + if illC: + R[k, J] = 0. + else: + R[k, J] = self.HFEngine.innerProduct(B[J], Q[k]) + B.data[:, J] -= np.outer(Q[k], R[k, J]) if only_R: return R for k in range(N - 1, -1, -1): J = list(range(k, N)) vtQ = self.HFEngine.innerProduct(Q[J], V[k]) Q.data[:, J] -= 2 * np.outer(V[k], vtQ) return Q, R diff --git a/rrompy/sampling/base/sampling_engine_base.py b/rrompy/sampling/base/sampling_engine_base.py index 4619855..19aa2bc 100644 --- a/rrompy/sampling/base/sampling_engine_base.py +++ b/rrompy/sampling/base/sampling_engine_base.py @@ -1,195 +1,189 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np -from rrompy.utilities.base.types import (Np1D, HFEng, strLst, paramVal, +from rrompy.utilities.base.types import (Np1D, HFEng, List, strLst, paramVal, paramList, sampList) -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.exception_manager import RROMPyWarning from rrompy.parameter import (emptyParameterList, checkParameter, checkParameterList) from rrompy.sampling import emptySampleList __all__ = ['SamplingEngineBase'] class SamplingEngineBase: """HERE""" def __init__(self, HFEngine:HFEng, verbosity : int = 10, timestamp : bool = True, allowRepeatedSamples : bool = True): self.verbosity = verbosity self.timestamp = timestamp self.allowRepeatedSamples = allowRepeatedSamples - if self.verbosity >= 10: - verbosityDepth("INIT", - "Initializing sampling engine of type {}.".format( - self.name()), - timestamp = self.timestamp) + vbMng(self, "INIT", + "Initializing sampling engine of type {}.".format(self.name()), + 10) self.HFEngine = HFEngine - if self.verbosity >= 10: - verbosityDepth("DEL", "Done initializing sampling engine.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done initializing sampling engine.", 10) def name(self) -> str: return self.__class__.__name__ def __str__(self) -> str: return self.name() def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) def resetHistory(self): self.samples = emptySampleList() self.nsamples = 0 self.mus = emptyParameterList() self._derIdxs = [] def popSample(self): if hasattr(self, "nsamples") and self.nsamples > 1: if self.samples.shape[1] > self.nsamples: RROMPyWarning(("More than 'nsamples' memory allocated for " "samples. Popping empty sample column.")) self.nsamples += 1 self.nsamples -= 1 self.samples.pop() self.mus.pop() else: self.resetHistory() def preallocateSamples(self, u:sampList, mu:paramVal, n:int): self.samples.reset((u.shape[0], n), u.dtype) self.samples[0] = u mu = checkParameter(mu, self.HFEngine.npar) self.mus.reset((n, self.HFEngine.npar)) self.mus[0] = mu[0] @property def HFEngine(self): """Value of HFEngine. Its assignment resets history.""" return self._HFEngine @HFEngine.setter def HFEngine(self, HFEngine): self._HFEngine = HFEngine self.resetHistory() def solveLS(self, mu : paramList = [], RHS : sampList = None, homogeneized : bool = False) -> sampList: """ Solve linear system. Args: mu: Parameter value. Returns: Solution of system. """ - mu, _ = checkParameterList(mu, self.HFEngine.npar) - if self.verbosity >= 15: - verbosityDepth("INIT", "Solving HF model for mu = {}.".format(mu), - timestamp = self.timestamp) + mu = checkParameterList(mu, self.HFEngine.npar)[0] + vbMng(self, "INIT", "Solving HF model for mu = {}.".format(mu), 15) u = self.HFEngine.solve(mu, RHS, homogeneized) - if self.verbosity >= 15: - verbosityDepth("DEL", "Done solving HF model.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done solving HF model.", 15) return u - def plotSamples(self, name : str = "u", save : str = None, - what : strLst = 'all', saveFormat : str = "eps", - saveDPI : int = 100, show : bool = True, **figspecs): + def plotSamples(self, warping : List[callable] = None, name : str = "u", + save : str = None, what : strLst = 'all', + saveFormat : str = "eps", saveDPI : int = 100, + show : bool = True, plotArgs : dict = {}, **figspecs): """ Do some nice plots of the samples. Args: + warping(optional): Domain warping functions. name(optional): Name to be shown as title of the plots. Defaults to 'u'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. what(optional): Which plots to do. If list, can contain 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. show(optional): Whether to show figure. Defaults to True. + plotArgs(optional): Optional arguments for fen/pyplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ for j in range(self.nsamples): - self.HFEngine.plot(self.samples[j], name = "{}_{}".format(name, j), - save = save, what = what, - saveFormat = saveFormat, saveDPI = saveDPI, - show = show, **figspecs) + self.HFEngine.plot(self.samples[j], warping, + "{}_{}".format(name, j), save, what, saveFormat, + saveDPI, show, plotArgs, **figspecs) def outParaviewSamples(self, name : str = "u", folders : bool = True, filename : str = "out", times : Np1D = None, what : strLst = 'all', forceNewFile : bool = True, filePW = None): """ Output samples to ParaView file. Args: name(optional): Base name to be used for data output. folders(optional): Whether to split output in folders. filename(optional): Name of output file. times(optional): Timestamps. what(optional): Which plots to do. If list, can contain 'MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. forceNewFile(optional): Whether to create new output file. filePW(optional): Fenics File entity (for time series). """ if times is None: times = [0.] * self.nsamples for j in range(self.nsamples): self.HFEngine.outParaview(self.samples[j], name = "{}_{}".format(name, j), filename = "{}_{}".format(filename, j), time = times[j], what = what, forceNewFile = forceNewFile, folder = folders, filePW = filePW) def outParaviewTimeDomainSamples(self, omegas : Np1D = None, timeFinal : Np1D = None, periodResolution : int = 20, name : str = "u", folders : bool = True, filename : str = "out", forceNewFile : bool = True): """ Output samples to ParaView file, converted to time domain. Args: omegas(optional): frequencies. timeFinal(optional): final time of simulation. periodResolution(optional): number of time steps per period. name(optional): Base name to be used for data output. folders(optional): Whether to split output in folders. filename(optional): Name of output file. forceNewFile(optional): Whether to create new output file. """ if omegas is None: omegas = np.real(self.mus) if not isinstance(timeFinal, (list, tuple,)): timeFinal = [timeFinal] * self.nsamples for j in range(self.nsamples): self.HFEngine.outParaviewTimeDomain(self.samples[j], omega = omegas[j], timeFinal = timeFinal[j], periodResolution = periodResolution, name = "{}_{}".format(name, j), filename = "{}_{}".format(filename, j), forceNewFile = forceNewFile, folder = folders) diff --git a/rrompy/sampling/base/sampling_engine_base_pivoted.py b/rrompy/sampling/base/sampling_engine_base_pivoted.py new file mode 100644 index 0000000..1d6c889 --- /dev/null +++ b/rrompy/sampling/base/sampling_engine_base_pivoted.py @@ -0,0 +1,208 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from rrompy.utilities.base.types import (Np1D, HFEng, List, ListAny, strLst, + paramVal, paramList, sampList) +from rrompy.utilities.base import verbosityManager as vbMng +from rrompy.utilities.exception_manager import RROMPyWarning +from rrompy.parameter import (emptyParameterList, checkParameter, + checkParameterList) +from rrompy.sampling import emptySampleList +from .sampling_engine_base import SamplingEngineBase + +__all__ = ['SamplingEngineBasePivoted'] + +class SamplingEngineBasePivoted(SamplingEngineBase): + """HERE""" + + def __init__(self, HFEngine:HFEng, directionPivot:ListAny, + verbosity : int = 10, timestamp : bool = True, + allowRepeatedSamples : bool = True): + super().__init__(HFEngine, verbosity, timestamp) + self.directionPivot = directionPivot + self.HFEngineMarginalized = None + self.resetHistory() + + @property + def directionMarginal(self): + return tuple([x for x in range(self.HFEngine.npar) \ + if x not in self.directionPivot]) + + @property + def nPivot(self): + return len(self.directionPivot) + + @property + def nMarginal(self): + return len(self.directionMarginal) + + @property + def nsamplesTot(self): + return np.sum(self.nsamples) + + def resetHistory(self, j : int = 1): + self.samples = [emptySampleList() for _ in range(j)] + self.nsamples = [0] * j + self.mus = [emptyParameterList() for _ in range(j)] + self._derIdxs = [[] for _ in range(j)] + + def popSample(self, j:int): + if hasattr(self, "nsamples") and self.nsamples[j] > 1: + if self.samples[j].shape[1] > self.nsamples[j]: + RROMPyWarning(("More than 'nsamples' memory allocated for " + "samples. Popping empty sample column.")) + self.nsamples[j] += 1 + self.nsamples[j] -= 1 + self.samples[j].pop() + self.mus[j].pop() + else: + self.resetHistory() + + def preallocateSamples(self, u:sampList, mu:paramVal, n:int, j:int): + self.samples[j].reset((u.shape[0], n), u.dtype) + self.samples[j][0] = u + mu = checkParameter(mu, self.nPivot) + self.mus[j].reset((n, self.nPivot)) + self.mus[j][0] = mu[0] + + def coalesceSamples(self): + self.samplesCoalesced = emptySampleList() + self.samplesCoalesced.reset((self.samples[0].shape[0], + np.sum([samp.shape[1] \ + for samp in self.samples])), + self.samples[0].dtype) + run_idx = 0 + for samp in self.samples: + slen = samp.shape[1] + self.samplesCoalesced.data[:, run_idx : run_idx + slen] = samp.data + run_idx += slen + + def solveLS(self, mu : paramList = [], RHS : sampList = None, + homogeneized : bool = False) -> sampList: + """ + Solve linear system. + + Args: + mu: Parameter value. + + Returns: + Solution of system. + """ + mu = checkParameterList(mu, self.nPivot)[0] + vbMng(self, "INIT", + ("Solving HF model for mu = {} for marginal " + "{}.").format(mu, self.HFEngineMarginalized.muFixed), 15) + u = self.HFEngineMarginalized.solve(mu, RHS, homogeneized) + vbMng(self, "DEL", "Done solving HF model.", 15) + return u + + def plotSamples(self, warping : List[callable] = None, name : str = "u", + save : str = None, what : strLst = 'all', + saveFormat : str = "eps", saveDPI : int = 100, + show : bool = True, plotArgs : dict = {}, **figspecs): + """ + Do some nice plots of the samples. + + Args: + warping(optional): Domain warping functions. + name(optional): Name to be shown as title of the plots. Defaults to + 'u'. + save(optional): Where to save plot(s). Defaults to None, i.e. no + saving. + what(optional): Which plots to do. If list, can contain 'ABS', + 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. + Defaults to 'ALL'. + saveFormat(optional): Format for saved plot(s). Defaults to "eps". + saveDPI(optional): DPI for saved plot(s). Defaults to 100. + show(optional): Whether to show figure. Defaults to True. + plotArgs(optional): Optional arguments for fen/pyplot. + figspecs(optional key args): Optional arguments for matplotlib + figure creation. + """ + for i in range(len(self.nsamples)): + for j in range(self.nsamples[i]): + self.HFEngine.plot(self.samples[i][j], warping, + "{}_{}_{}".format(name, i, j), save, what, + saveFormat, saveDPI, show, plotArgs, + **figspecs) + + def outParaviewSamples(self, name : str = "u", folders : bool = True, + filename : str = "out", times : Np1D = None, + what : strLst = 'all', forceNewFile : bool = True, + filePW = None): + """ + Output samples to ParaView file. + + Args: + name(optional): Base name to be used for data output. + folders(optional): Whether to split output in folders. + filename(optional): Name of output file. + times(optional): Timestamps. + what(optional): Which plots to do. If list, can contain 'MESH', + 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard + 'ALL'. Defaults to 'ALL'. + forceNewFile(optional): Whether to create new output file. + filePW(optional): Fenics File entity (for time series). + """ + if times is None: times = [[0.] * self.nsamples[i] \ + for i in range(len(self.nsamples))] + for i in range(len(self.nsamples)): + for j in range(self.nsamples[i]): + self.HFEngine.outParaview(self.samples[i][j], + name = "{}_{}_{}".format(name, i, j), + filename = "{}_{}_{}".format(filename, + i, j), + time = times[i][j], what = what, + forceNewFile = forceNewFile, + folder = folders, filePW = filePW) + + def outParaviewTimeDomainSamples(self, omegas : Np1D = None, + timeFinal : Np1D = None, + periodResolution : int = 20, + name : str = "u", folders : bool = True, + filename : str = "out", + forceNewFile : bool = True): + """ + Output samples to ParaView file, converted to time domain. + + Args: + omegas(optional): frequencies. + timeFinal(optional): final time of simulation. + periodResolution(optional): number of time steps per period. + name(optional): Base name to be used for data output. + folders(optional): Whether to split output in folders. + filename(optional): Name of output file. + forceNewFile(optional): Whether to create new output file. + """ + if omegas is None: omegas = [[np.real(self.mus[i])] \ + for i in range(len(self.nsamples))] + if not isinstance(timeFinal, (list, tuple,)): + timeFinal = [[timeFinal] * self.nsamples[i] \ + for i in range(len(self.nsamples))] + for i in range(len(self.nsamples)): + for j in range(self.nsamples[i]): + self.HFEngine.outParaviewTimeDomain(self.samples[i][j], + omega = omegas[i][j], + timeFinal = timeFinal[i][j], + periodResolution = periodResolution, + name = "{}_{}_{}".format(name, i, j), + filename = "{}_{}_{}".format(filename, + i, j), + forceNewFile = forceNewFile, + folder = folders) diff --git a/rrompy/sampling/linear_problem/__init__.py b/rrompy/sampling/pivoted/__init__.py similarity index 79% rename from rrompy/sampling/linear_problem/__init__.py rename to rrompy/sampling/pivoted/__init__.py index 8326efd..d69531e 100644 --- a/rrompy/sampling/linear_problem/__init__.py +++ b/rrompy/sampling/pivoted/__init__.py @@ -1,27 +1,27 @@ # 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_linear import SamplingEngineLinear -from .sampling_engine_linear_pod import SamplingEngineLinearPOD +from .sampling_engine_pivoted import SamplingEnginePivoted +from .sampling_engine_pivoted_pod import SamplingEnginePivotedPOD __all__ = [ - 'SamplingEngineLinear', - 'SamplingEngineLinearPOD' + 'SamplingEnginePivoted', + 'SamplingEnginePivotedPOD' ] diff --git a/rrompy/sampling/pivoted/sampling_engine_pivoted.py b/rrompy/sampling/pivoted/sampling_engine_pivoted.py new file mode 100644 index 0000000..2119cb4 --- /dev/null +++ b/rrompy/sampling/pivoted/sampling_engine_pivoted.py @@ -0,0 +1,128 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +from copy import deepcopy as copy +import numpy as np +from rrompy.sampling.base.sampling_engine_base_pivoted import ( + SamplingEngineBasePivoted) +from rrompy.hfengines.base import MarginalProxyEngine +from rrompy.utilities.base.types import Np1D, paramVal, paramList, sampList +from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp +from rrompy.utilities.exception_manager import RROMPyException +from rrompy.utilities.poly_fitting.polynomial import nextDerivativeIndices +from rrompy.parameter import checkParameter, checkParameterList +from rrompy.sampling import sampleList + +__all__ = ['SamplingEnginePivoted'] + +class SamplingEnginePivoted(SamplingEngineBasePivoted): + """HERE""" + + def preprocesssamples(self, idxs:Np1D, j:int) -> sampList: + if self.samples[j] is None or len(self.samples[j]) == 0: return + return self.samples[j](idxs) + + def postprocessu(self, u:sampList, j:int, + overwrite : bool = False) -> Np1D: + return copy(u) + + def postprocessuBulk(self, u:sampList, j:int) -> sampList: + return copy(u) + + def lastSampleManagement(self, j:int): + pass + + def _getSampleConcurrence(self, mu:paramVal, j:int, previous:Np1D, + homogeneized : bool = False) -> sampList: + if len(previous) >= len(self._derIdxs[j]): + self._derIdxs[j] += nextDerivativeIndices( + self._derIdxs[j], self.nPivot, + len(previous) + 1 - len(self._derIdxs[j])) + derIdx = self._derIdxs[j][len(previous)] + mu = checkParameter(mu, self.nPivot) + samplesOld = self.preprocesssamples(previous, j) + RHS = self.HFEngineMarginalized.b(mu, derIdx, + homogeneized = homogeneized) + for j, derP in enumerate(self._derIdxs[j][: len(previous)]): + diffP = [x - y for (x, y) in zip(derIdx, derP)] + if np.all([x >= 0 for x in diffP]): + RHS -= self.HFEngineMarginalized.A(mu, diffP).dot( + samplesOld[j]) + return self.solveLS(mu, RHS = RHS, homogeneized = homogeneized) + + def nextSample(self, mu:paramVal, j:int, overwrite : bool = False, + homogeneized : bool = False, + lastSample : bool = True) -> Np1D: + mu = checkParameter(mu, self.nPivot) + ns = self.nsamples[j] + muidxs = self.mus[j].findall(mu[0]) + if len(muidxs) > 0: + u = self._getSampleConcurrence(mu, j, np.sort(muidxs), + homogeneized) + else: + u = self.solveLS(mu, homogeneized = homogeneized) + u = self.postprocessu(u, j, overwrite = overwrite) + if overwrite: + self.samples[j][ns] = u + self.mus[j][ns] = mu[0] + else: + if ns == 0: + self.samples[j] = sampleList(u) + else: + self.samples[j].append(u) + self.mus[j].append(mu) + self.nsamples[j] += 1 + if lastSample: self.lastSampleManagement(j) + return u + + def iterSample(self, mus:paramList, musM:paramList, + homogeneized : bool = False) -> sampList: + mus = checkParameterList(mus, self.nPivot)[0] + musM = checkParameterList(musM, self.nMarginal)[0] + vbMng(self, "INIT", "Starting sampling iterations.", 5) + n = len(mus) + m = len(musM) + if n <= 0: + raise RROMPyException("Number of samples must be positive.") + if m <= 0: + raise RROMPyException(("Number of marginal samples must be " + "positive.")) + for j in range(m): + muMEff = [fp] * self.HFEngine.npar + for k, x in enumerate(self.directionMarginal): + muMEff[x] = musM(j, k) + self.HFEngineMarginalized = MarginalProxyEngine(self.HFEngine, + list(muMEff)) + if self.allowRepeatedSamples: + for k in range(n): + vbMng(self, "MAIN", + "Computing sample {} / {} for marginal {} / {}."\ + .format(k + 1, n, j, m), 10) + self.nextSample(mus[k], j, overwrite = (k > 0), + homogeneized = homogeneized, + lastSample = (n == k + 1)) + if n > 1 and k == 0: + self.preallocateSamples(self.samples[j][0], mus[0], + n, j) + else: + self.samples[j] = self.postprocessuBulk(self.solveLS(mus, + homogeneized = homogeneized), j) + self.mus[j] = copy(mus) + self.nsamples[j] = n + vbMng(self, "DEL", "Finished sampling iterations.", 5) + return self.samples[j] diff --git a/rrompy/sampling/pivoted/sampling_engine_pivoted_pod.py b/rrompy/sampling/pivoted/sampling_engine_pivoted_pod.py new file mode 100644 index 0000000..af32fb3 --- /dev/null +++ b/rrompy/sampling/pivoted/sampling_engine_pivoted_pod.py @@ -0,0 +1,114 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from copy import deepcopy as copy +from rrompy.sampling.base.pod_engine import PODEngine +from .sampling_engine_pivoted import SamplingEnginePivoted +from rrompy.utilities.base.types import Np1D, paramVal, sampList +from rrompy.utilities.base import verbosityManager as vbMng +from rrompy.sampling import sampleList, emptySampleList + +__all__ = ['SamplingEnginePivotedPOD'] + +class SamplingEnginePivotedPOD(SamplingEnginePivoted): + """HERE""" + + def resetHistory(self, j : int = 1): + super().resetHistory(j) + self.samples_full = [None] * j + self.RPOD = [None] * j + + def popSample(self, j:int): + if hasattr(self, "nsamples") and self.nsamples[j] > 1: + self.RPOD[j] = self.RPOD[j][: -1, : -1] + self.samples_full[j].pop() + super().popSample(j) + + def coalesceSamples(self, tol : float = 1e-12): + super().coalesceSamples() + self.samplesCoalesced, RPODC = ( + self.PODEngine.generalizedQR(self.samplesCoalesced)) + self.RPODCoalesced = np.zeros((self.samplesCoalesced.shape[1], + self.samplesCoalesced.shape[1]), + dtype = self.RPOD[0].dtype) + self.samples_fullCoalesced = emptySampleList() + self.samples_fullCoalesced.reset((self.samples_full[0].shape[0], + self.samplesCoalesced.shape[1]), + self.samples_full[0].dtype) + ci = 0 + for j, (Rloc, samp) in enumerate(zip(self.RPOD, self.samples_full)): + ri = 0 + Rheg = Rloc.shape[1] + for k, Rloc2 in enumerate(self.RPOD[: j + 1]): + Rlen = Rloc2.shape[1] + self.RPODCoalesced[ri : ri + Rlen, ci : ci + Rheg] = ( + RPODC[ri : ri + Rlen, ci : ci + Rheg].dot(Rloc)) + ri += Rlen + self.samples_fullCoalesced.data[:, ci : ci + Rheg] = samp.data + ci += Rheg + RCdiag = np.abs(np.diag(self.RPODCoalesced)) + RCdiag /= RCdiag[0] + ntrunc = np.nonzero(RCdiag < tol)[0] + if len(ntrunc) == 0: return + self.samplesCoalesced.data = self.samplesCoalesced.data[:, : ntrunc[0]] + self.RPODCoalesced = self.RPODCoalesced[: ntrunc[0], :] + + @property + def HFEngine(self): + """Value of HFEngine. Its assignment resets history.""" + return self._HFEngine + @HFEngine.setter + def HFEngine(self, HFEngine): + self._HFEngine = HFEngine + self.resetHistory() + self.PODEngine = PODEngine(self._HFEngine) + + def preprocesssamples(self, idxs:Np1D, j:int) -> sampList: + if self.samples_full[j] is None or len(self.samples_full[j]) == 0: + return + return self.samples_full[j](idxs) + + def postprocessu(self, u:sampList, j:int, + overwrite : bool = False) -> Np1D: + ns = self.nsamples[j] + if overwrite: + self.samples_full[j][ns] = copy(u) + else: + if ns == 0: + self.samples_full[j] = sampleList(u) + else: + self.samples_full[j].append(u) + return u + + def postprocessuBulk(self, u:sampList, j:int) -> sampList: + self.samples_full[j] = copy(u) + vbMng(self, "INIT", + "Starting orthogonalization for marginal {}.".format(j), 40) + u, self.RPOD[j] = self.PODEngine.generalizedQR(self.samples_full[j]) + vbMng(self, "DEL", "Done orthogonalizing.", 40) + return u + + def lastSampleManagement(self, j:int): + self.samples[j] = self.postprocessuBulk(self.samples_full[j], j) + + def preallocateSamples(self, u:Np1D, mu:paramVal, n:int, j:int): + super().preallocateSamples(u, mu, n, j) + self.samples_full[j].reset((u.shape[0], n), u.dtype) + self.samples_full[j][0] = u + diff --git a/rrompy/reduction_methods/centered/__init__.py b/rrompy/sampling/standard/__init__.py similarity index 76% rename from rrompy/reduction_methods/centered/__init__.py rename to rrompy/sampling/standard/__init__.py index 633b633..6d58807 100644 --- a/rrompy/reduction_methods/centered/__init__.py +++ b/rrompy/sampling/standard/__init__.py @@ -1,29 +1,27 @@ # 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_centered_approximant import GenericCenteredApproximant -from .rational_pade import RationalPade -from .rb_centered import RBCentered +from .sampling_engine_standard import SamplingEngineStandard +from .sampling_engine_standard_pod import SamplingEngineStandardPOD __all__ = [ - 'GenericCenteredApproximant', - 'RationalPade', - 'RBCentered' + 'SamplingEngineStandard', + 'SamplingEngineStandardPOD' ] diff --git a/rrompy/sampling/linear_problem/sampling_engine_linear.py b/rrompy/sampling/standard/sampling_engine_standard.py similarity index 84% rename from rrompy/sampling/linear_problem/sampling_engine_linear.py rename to rrompy/sampling/standard/sampling_engine_standard.py index cdd7246..50ab0f7 100644 --- a/rrompy/sampling/linear_problem/sampling_engine_linear.py +++ b/rrompy/sampling/standard/sampling_engine_standard.py @@ -1,118 +1,112 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import deepcopy as copy import numpy as np from rrompy.sampling.base.sampling_engine_base import SamplingEngineBase from rrompy.utilities.base.types import Np1D, paramVal, paramList, sampList -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.exception_manager import RROMPyException from rrompy.utilities.poly_fitting.polynomial import nextDerivativeIndices from rrompy.parameter import checkParameter, checkParameterList from rrompy.sampling import sampleList -__all__ = ['SamplingEngineLinear'] +__all__ = ['SamplingEngineStandard'] -class SamplingEngineLinear(SamplingEngineBase): +class SamplingEngineStandard(SamplingEngineBase): """HERE""" def preprocesssamples(self, idxs:Np1D) -> sampList: if self.samples is None or len(self.samples) == 0: return return self.samples(idxs) def postprocessu(self, u:sampList, overwrite : bool = False) -> Np1D: return copy(u) def postprocessuBulk(self, u:sampList) -> sampList: return copy(u) def lastSampleManagement(self): pass def _getSampleConcurrence(self, mu:paramVal, previous:Np1D, homogeneized : bool = False) -> sampList: if len(previous) >= len(self._derIdxs): self._derIdxs += nextDerivativeIndices(self._derIdxs, self.HFEngine.npar, len(previous) + 1 - len(self._derIdxs)) derIdx = self._derIdxs[len(previous)] mu = checkParameter(mu, self.HFEngine.npar) samplesOld = self.preprocesssamples(previous) RHS = self.HFEngine.b(mu, derIdx, homogeneized = homogeneized) for j, derP in enumerate(self._derIdxs[: len(previous)]): diffP = [x - y for (x, y) in zip(derIdx, derP)] if np.all([x >= 0 for x in diffP]): RHS -= self.HFEngine.A(mu, diffP).dot(samplesOld[j]) return self.solveLS(mu, RHS = RHS, homogeneized = homogeneized) def nextSample(self, mu : paramVal = [], overwrite : bool = False, homogeneized : bool = False, lastSample : bool = True) -> Np1D: mu = checkParameter(mu, self.HFEngine.npar) ns = self.nsamples muidxs = self.mus.findall(mu[0]) if len(muidxs) > 0: u = self._getSampleConcurrence(mu, np.sort(muidxs), homogeneized) else: u = self.solveLS(mu, homogeneized = homogeneized) u = self.postprocessu(u, overwrite = overwrite) if overwrite: self.samples[ns] = u self.mus[ns] = mu[0] else: if ns == 0: self.samples = sampleList(u) else: self.samples.append(u) self.mus.append(mu) self.nsamples += 1 if lastSample: self.lastSampleManagement() return u def iterSample(self, mus:paramList, homogeneized : bool = False) -> sampList: - mus, _ = checkParameterList(mus, self.HFEngine.npar) - if self.verbosity >= 5: - verbosityDepth("INIT", "Starting sampling iterations.", - timestamp = self.timestamp) + mus = checkParameterList(mus, self.HFEngine.npar)[0] + vbMng(self, "INIT", "Starting sampling iterations.", 5) n = len(mus) if n <= 0: raise RROMPyException(("Number of samples must be positive.")) self.resetHistory() if self.allowRepeatedSamples: for j in range(n): - if self.verbosity >= 7: - verbosityDepth("MAIN", ("Computing sample " - "{} / {}.").format(j + 1, n), - timestamp = self.timestamp) + vbMng(self, "MAIN", + "Computing sample {} / {}.".format(j + 1, n), 7) self.nextSample(mus[j], overwrite = (j > 0), homogeneized = homogeneized, lastSample = (n == j + 1)) - if j == 0: + if n > 1 and j == 0: self.preallocateSamples(self.samples[0], mus[0], n) else: self.samples = self.postprocessuBulk(self.solveLS(mus, homogeneized = homogeneized)) self.mus = copy(mus) self.nsamples = n - if self.verbosity >= 5: - verbosityDepth("DEL", "Finished sampling iterations.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Finished sampling iterations.", 5) return self.samples diff --git a/rrompy/sampling/linear_problem/sampling_engine_linear_pod.py b/rrompy/sampling/standard/sampling_engine_standard_pod.py similarity index 83% rename from rrompy/sampling/linear_problem/sampling_engine_linear_pod.py rename to rrompy/sampling/standard/sampling_engine_standard_pod.py index bcfea23..1b53583 100644 --- a/rrompy/sampling/linear_problem/sampling_engine_linear_pod.py +++ b/rrompy/sampling/standard/sampling_engine_standard_pod.py @@ -1,84 +1,80 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import deepcopy as copy from rrompy.sampling.base.pod_engine import PODEngine -from .sampling_engine_linear import SamplingEngineLinear +from .sampling_engine_standard import SamplingEngineStandard from rrompy.utilities.base.types import Np1D, paramVal, sampList -from rrompy.utilities.base import verbosityDepth +from rrompy.utilities.base import verbosityManager as vbMng from rrompy.sampling import sampleList -__all__ = ['SamplingEngineLinearPOD'] +__all__ = ['SamplingEngineStandardPOD'] -class SamplingEngineLinearPOD(SamplingEngineLinear): +class SamplingEngineStandardPOD(SamplingEngineStandard): """HERE""" def resetHistory(self): super().resetHistory() self.samples_full = None self.RPOD = None def popSample(self): if hasattr(self, "nsamples") and self.nsamples > 1: self.RPOD = self.RPOD[: -1, : -1] self.samples_full.pop() super().popSample() @property def HFEngine(self): """Value of HFEngine. Its assignment resets history.""" return self._HFEngine @HFEngine.setter def HFEngine(self, HFEngine): self._HFEngine = HFEngine self.resetHistory() self.PODEngine = PODEngine(self._HFEngine) def preprocesssamples(self, idxs:Np1D) -> sampList: if self.samples_full is None or len(self.samples_full) == 0: return return self.samples_full(idxs) def postprocessu(self, u:sampList, overwrite : bool = False) -> Np1D: ns = self.nsamples if overwrite: self.samples_full[ns] = copy(u) else: if ns == 0: self.samples_full = sampleList(u) else: self.samples_full.append(u) return u def postprocessuBulk(self, u:sampList) -> sampList: self.samples_full = copy(u) - if self.verbosity >= 10: - verbosityDepth("INIT", "Starting orthogonalization.", - timestamp = self.timestamp) + vbMng(self, "INIT", "Starting orthogonalization.", 10) u, self.RPOD = self.PODEngine.generalizedQR(self.samples_full) - if self.verbosity >= 10: - verbosityDepth("DEL", "Done orthogonalizing.", - timestamp = self.timestamp) + vbMng(self, "DEL", "Done orthogonalizing.", 10) return u def lastSampleManagement(self): self.samples = self.postprocessuBulk(self.samples_full) def preallocateSamples(self, u:Np1D, mu:paramVal, n:int): super().preallocateSamples(u, mu, n) self.samples_full.reset((u.shape[0], n), u.dtype) self.samples_full[0] = u diff --git a/rrompy/utilities/base/__init__.py b/rrompy/utilities/base/__init__.py index 6bcf126..c2be650 100644 --- a/rrompy/utilities/base/__init__.py +++ b/rrompy/utilities/base/__init__.py @@ -1,55 +1,42 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from .find_dict_str_key import findDictStrKey from .get_new_filename import getNewFilename -from .kroneckerer import kroneckerer -from .factorials import multibinom, multifactorial from .pickle_utilities import pickleDump, pickleLoad from .purge_dict import purgeDict from .purge_list import purgeList -from .number_theory import (squareResonances, primeFactorize, - getLowestPrimeFactor) -from .halton import haltonGenerate -from .sobol import sobolGenerate -from .low_discrepancy import vanderCorput, lowDiscrepancy from . import types as Types -from .verbosity_depth import verbosityDepth +from .verbosity_depth import verbosityDepth, verbosityManager + +freepar = None __all__ = [ 'findDictStrKey', 'getNewFilename', - 'kroneckerer', - 'multibinom', - 'multifactorial', 'pickleDump', 'pickleLoad', 'purgeDict', 'purgeList', - 'squareResonances', - 'primeFactorize', - 'getLowestPrimeFactor', - 'haltonGenerate', - 'sobolGenerate', - 'vanderCorput', - 'lowDiscrepancy', 'Types', - 'verbosityDepth' + 'verbosityDepth', + 'verbosityManager', + 'freepar' ] diff --git a/rrompy/utilities/base/types.py b/rrompy/utilities/base/types.py index b84e189..ced3eef 100644 --- a/rrompy/utilities/base/types.py +++ b/rrompy/utilities/base/types.py @@ -1,61 +1,61 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from typing import TypeVar, List, Tuple, Dict, Any __all__ = ['TupleAny','ListAny','DictAny','ScOp','Np1D','Np2D','Np1DLst', 'N2FSExpr','FenExpr','FenFunc','FenFuncSpace','FenBC','HFEng', - 'ROMEng','sampleEng','normEng','paramVal','paramList', 'sampList', - 'GenExpr','strLst', 'BfSExpr'] + 'ROMEng','sampleEng','normEng','interpEng','paramVal','paramList', + 'sampList','GenExpr','strLst', 'BfSExpr'] # ANY TupleAny = Tuple[Any] ListAny = List[Any] DictAny = Dict[Any, Any] # SCIPY ScOp = TypeVar("Scipy sparse matrix for space operator") # NUMPY Np1D = TypeVar("NumPy 1D array") Np2D = TypeVar("NumPy 2D array-like") Np1DLst = TypeVar("NumPy 1D array or list of NumPy 1D array") N2FSExpr = TypeVar("NumPy 2D array, float or str") # FENICS FenExpr = TypeVar("FEniCS expression") FenFunc = TypeVar("FEniCS function") FenFuncSpace = TypeVar("FEniCS function space") FenBC = TypeVar("FEniCS boundary condition") # ENGINES HFEng = TypeVar("High fidelity engine") ROMEng = TypeVar("ROM engine") sampleEng = TypeVar("Sampling engine") normEng = TypeVar("Norm engine") +interpEng = TypeVar("Interpolator engine") # CUSTOM TYPES paramVal = TypeVar("Parameter value tuple") paramList = TypeVar("Parameter value tuple list") sampList = TypeVar("Sample list") -radialFun = TypeVar("Radial basis function") # OTHERS GenExpr = TypeVar("Generic expression") strLst = TypeVar("str or list of str") BfSExpr = TypeVar("Boolean function or string") diff --git a/rrompy/utilities/base/verbosity_depth.py b/rrompy/utilities/base/verbosity_depth.py index 10626e6..483b2dc 100644 --- a/rrompy/utilities/base/verbosity_depth.py +++ b/rrompy/utilities/base/verbosity_depth.py @@ -1,53 +1,59 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from rrompy.utilities.exception_manager import RROMPyException -__all__ = ["verbosityDepth"] +__all__ = ["verbosityDepth", "verbosityManager"] from datetime import datetime def getTimestamp() -> str: return "\x1b[42m{}\x1b[0m".format(datetime.now().strftime("%H:%M:%S.%f")) def verbosityDepth(vdtype:str, message:str, end : str = "\n", timestamp : bool = True): global RROMPy_verbosity_depth assert isinstance(vdtype, str) if vdtype.upper() not in ["INIT", "MAIN", "DEL"]: raise RROMPyException("Verbosity depth type not recognized.") out = "{} ".format(getTimestamp()) if timestamp else "" if vdtype == "INIT": if "RROMPy_verbosity_depth" not in globals(): RROMPy_verbosity_depth = 0 RROMPy_verbosity_depth += 1 out += "│" * (RROMPy_verbosity_depth - 1) out += "┌" else: assert "RROMPy_verbosity_depth" in globals() if vdtype == "MAIN": out += "│" * (RROMPy_verbosity_depth - 1) out += "├" elif vdtype == "DEL": RROMPy_verbosity_depth -= 1 out += "│" * RROMPy_verbosity_depth out += "└" if RROMPy_verbosity_depth <= 0: del RROMPy_verbosity_depth if message != "": print("{}{}".format(out, message), end = end) return + +def verbosityManager(object, vdtype:str, message:str, vlvl : int = 0, + end : str = "\n"): + if object.verbosity >= vlvl: + return verbosityDepth(vdtype, message, end, object.timestamp) + diff --git a/rrompy/utilities/exception_manager/exception_manager.py b/rrompy/utilities/exception_manager/exception_manager.py index 89de98e..c019850 100644 --- a/rrompy/utilities/exception_manager/exception_manager.py +++ b/rrompy/utilities/exception_manager/exception_manager.py @@ -1,22 +1,32 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # __all__ = ["RROMPyException"] +def purgeVerbosityDepth(): + from rrompy.utilities.base.verbosity_depth import verbosityDepth + while True: + try: + verbosityDepth("DEL", "", "", False) + except: + break + class RROMPyException(Exception): - pass + def __init__(self, message): + purgeVerbosityDepth() + super().__init__(message) diff --git a/rrompy/utilities/base/__init__.py b/rrompy/utilities/numerical/__init__.py similarity index 58% copy from rrompy/utilities/base/__init__.py copy to rrompy/utilities/numerical/__init__.py index 6bcf126..6af5afa 100644 --- a/rrompy/utilities/base/__init__.py +++ b/rrompy/utilities/numerical/__init__.py @@ -1,55 +1,49 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -from .find_dict_str_key import findDictStrKey -from .get_new_filename import getNewFilename -from .kroneckerer import kroneckerer +from .custom_pinv import customPInv from .factorials import multibinom, multifactorial -from .pickle_utilities import pickleDump, pickleLoad -from .purge_dict import purgeDict -from .purge_list import purgeList -from .number_theory import (squareResonances, primeFactorize, - getLowestPrimeFactor) from .halton import haltonGenerate +from .kroneckerer import kroneckerer +from .low_discrepancy import lowDiscrepancy +from .marginalize_poly_list import marginalizePolyList +from .nonlinear_eigenproblem import (linearizeDense, eigNonlinearDense, + eigvalsNonlinearDense) +from .number_theory import squareResonances +from .point_matching import pointMatching from .sobol import sobolGenerate -from .low_discrepancy import vanderCorput, lowDiscrepancy -from . import types as Types -from .verbosity_depth import verbosityDepth + +freepar = None __all__ = [ - 'findDictStrKey', - 'getNewFilename', - 'kroneckerer', + 'customPInv', 'multibinom', 'multifactorial', - 'pickleDump', - 'pickleLoad', - 'purgeDict', - 'purgeList', - 'squareResonances', - 'primeFactorize', - 'getLowestPrimeFactor', 'haltonGenerate', - 'sobolGenerate', - 'vanderCorput', + 'kroneckerer', 'lowDiscrepancy', - 'Types', - 'verbosityDepth' + 'marginalizePolyList', + 'linearizeDense', + 'eigNonlinearDense', + 'eigvalsNonlinearDense', + 'squareResonances', + 'pointMatching', + 'sobolGenerate' ] diff --git a/rrompy/utilities/poly_fitting/custom_pinv.py b/rrompy/utilities/numerical/custom_pinv.py similarity index 100% rename from rrompy/utilities/poly_fitting/custom_pinv.py rename to rrompy/utilities/numerical/custom_pinv.py diff --git a/rrompy/utilities/base/factorials.py b/rrompy/utilities/numerical/factorials.py similarity index 100% rename from rrompy/utilities/base/factorials.py rename to rrompy/utilities/numerical/factorials.py diff --git a/rrompy/utilities/base/halton.py b/rrompy/utilities/numerical/halton.py similarity index 100% rename from rrompy/utilities/base/halton.py rename to rrompy/utilities/numerical/halton.py diff --git a/rrompy/utilities/base/kroneckerer.py b/rrompy/utilities/numerical/kroneckerer.py similarity index 100% rename from rrompy/utilities/base/kroneckerer.py rename to rrompy/utilities/numerical/kroneckerer.py diff --git a/rrompy/utilities/base/low_discrepancy.py b/rrompy/utilities/numerical/low_discrepancy.py similarity index 96% rename from rrompy/utilities/base/low_discrepancy.py rename to rrompy/utilities/numerical/low_discrepancy.py index 0dc73eb..e765fee 100644 --- a/rrompy/utilities/base/low_discrepancy.py +++ b/rrompy/utilities/numerical/low_discrepancy.py @@ -1,42 +1,42 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.utilities.base.types import List from rrompy.utilities.exception_manager import RROMPyException -__all__ = ['vanderCorput', 'lowDiscrepancy'] +__all__ = ['lowDiscrepancy'] def vanderCorput(n:int) -> List[int]: if n <= 0: raise RROMPyException("Only positive integers allowed.") x = [0] * n ln = int(np.ceil(np.log2(n))) for j in range(n): x[j] = int(np.binary_repr(j, width = ln)[::-1], 2) return x def lowDiscrepancy(n:int, inverse : bool = False) -> List[int]: if n <= 0: raise RROMPyException("Only positive integers allowed.") max2Fac = 2 ** int(np.ceil(np.log2(n))) xBase = np.array(vanderCorput(max2Fac), dtype = np.int) x = list(xBase[xBase < n]) if inverse: x = list(np.argsort(x)) return x diff --git a/rrompy/utilities/numerical/marginalize_poly_list.py b/rrompy/utilities/numerical/marginalize_poly_list.py new file mode 100644 index 0000000..9780834 --- /dev/null +++ b/rrompy/utilities/numerical/marginalize_poly_list.py @@ -0,0 +1,79 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from scipy.sparse import csr +from rrompy.utilities.base.types import Np1D, Np2D, ListAny +from rrompy.utilities.base import freepar as fp +from rrompy.utilities.poly_fitting.polynomial import ( + hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) +from rrompy.parameter import checkParameter + +__all__ = ['marginalizePolyList'] + +def marginalizePolyList(objs:ListAny, marginalVals : Np1D = [fp], + zeroObj : Np2D = 0., + recompress : bool = True) -> ListAny: + res = [] + freeLocations = [] + fixedLocations = [] + muFixed = [] + if not hasattr(marginalVals, "__len__"): marginalVals = [marginalVals] + for i, m in enumerate(marginalVals): + if m == fp: + freeLocations += [i] + else: + fixedLocations += [i] + muFixed += [m] + muFixed = checkParameter(muFixed, len(fixedLocations)) + if zeroObj == "auto": + if isinstance(objs[0], np.ndarray): + zeroObj = np.zeros_like(objs[0]) + elif isinstance(objs[0], csr.csr_matrix): + d = objs[0].shape[0] + zeroObj = csr.csr_matrix(([], [], np.zeros(d + 1)), + shape = objs[0].shape, + dtype = objs[0].dtype) + else: + zeroObj = 0. + for j, obj in enumerate(objs): + derjBase = hashI(j, len(marginalVals)) + jNew = hashD([derjBase[i] for i in freeLocations]) + derjFixed = [derjBase[i] for i in fixedLocations] + obj = np.prod((muFixed ** derjFixed).data) * obj + if jNew >= len(res): + for _ in range(len(res), jNew): + res += [zeroObj] + res += [obj] + else: + res[jNew] = res[jNew] + obj + if recompress: + for re in res[::-1]: + try: + if isinstance(re, np.ndarray): + iszero = np.allclose(re, zeroObj, + atol = 2 * np.finfo(re.dtype).eps) + elif isinstance(re, csr.csr_matrix): + iszero = re.nnz == 0 + else: + break + if not iszero: break + except: break + res.pop() + return res + \ No newline at end of file diff --git a/rrompy/utilities/numerical/nonlinear_eigenproblem.py b/rrompy/utilities/numerical/nonlinear_eigenproblem.py new file mode 100644 index 0000000..73f1e2d --- /dev/null +++ b/rrompy/utilities/numerical/nonlinear_eigenproblem.py @@ -0,0 +1,111 @@ +# 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.linalg as scla +#import scipy.sparse as scsp +from rrompy.utilities.base.types import Tuple, List, Np1D, Np2D +from .custom_pinv import customPInv + +__all__ = ['linearizeDense', 'eigNonlinearDense', 'eigvalsNonlinearDense'] + +def linearizeDense(As:List[Np2D], jSupp : int = 1) -> Tuple[Np2D, Np2D]: + N = len(As) + n = As[0].shape[0] + stiff = np.zeros(((N - 1) * n, (N - 1) * n), dtype = As[0].dtype) + mass = np.zeros(((N - 1) * n, (N - 1) * n), dtype = As[0].dtype) + if N > 1: + if isinstance(jSupp, str) and jSupp.upper() == "COMPANION": + II = np.arange(n, (N - 1) * n) + stiff = np.pad(- np.hstack(As[-2 :: -1]), + [[0, (N - 2) * n], [0, 0]], "constant") + stiff[II, II - n] = 1. + mass = np.pad(As[-1], [0, (N - 2) * n], "constant") + mass[II, II] = 1. + else: + for j in range(jSupp): + for k in range(jSupp - j - 1, jSupp): + mass[n * j : n * (j + 1), k * n : (k + 1) * n] = \ + As[N - 2 + jSupp - k - j] + for j in range(jSupp - 1, N - 1): + for k in range(jSupp, N - 1 + jSupp - j): + stiff[n * j : n * (j + 1), (k - 1) * n : k * n] = \ + - As[jSupp - k + N - 2 - j] + stiff[: n * (jSupp - 1), : n * (jSupp - 1)] = \ + mass[: n * (jSupp - 1), n : n * jSupp] + mass[n * jSupp :, n * jSupp :] = stiff[n * (jSupp - 1) : - n, + n * jSupp :] + return stiff, mass + +def eigNonlinearDense(As:List[Np2D], jSupp : int = 1, + return_inverse : bool = False, + **kwargs_eig) -> Tuple[Np1D, Np2D]: + stiff, mass = linearizeDense(As, jSupp) + if stiff.shape[0] == 0: return stiff, stiff + ev, P = scla.eig(stiff, mass, overwrite_a = True, overwrite_b = True, + **kwargs_eig) + if not return_inverse: return ev, P + Pinv = customPInv(P) + return ev, P, Pinv + +def eigvalsNonlinearDense(As:List[Np2D], jSupp : int = 1, + **kwargs_eigvals) -> Np1D: + stiff, mass = linearizeDense(As, jSupp) + if stiff.shape[0] == 0: return stiff + return scla.eigvals(stiff, mass, overwrite_a = True, **kwargs_eigvals) + +#def linearizeSparse(As:List[Np2D], jSupp : int = 1) -> Tuple[Np2D, Np2D]: +# N = len(As) +# n = As[0].shape[0] +# if isinstance(jSupp, str) and jSupp.upper() == "COMPANION": +# II = np.arange(n, (N - 1) * n) +# III = np.arange((N - 2) * n + 1) +# IIII = np.arange(0, n ** 2, n) +# improve management of sparse As... +# Alist = - np.hstack([A.todense() for A in As[-2 :: -1]]) +# stiffD = np.concatenate((Alist.flatten(), np.ones((N - 2) * n))) +# stiffP = np.concatenate(((N - 1) * IIII, (N - 1) * n ** 2 + III)) +# stiffI = np.concatenate((np.tile(np.arange((N - 1) * n), n), II - n)) +# massD = np.concatenate((As[-1].todense().flatten(), +# np.ones((N - 2) * n))) +# massP = np.concatenate((IIII, n ** 2 + III)) +# massI = np.concatenate((np.tile(np.arange(n), n), II)) +# else: +# compute stiffD, stiffP, stiffI depending on jSupp +# compute massD, massP, massI depending on jSupp +# stiff = scsp.csr_matrix((stiffD, stiffI, stiffP), +# shape = ((N - 1) * n, (N - 1) * n)) +# mass = scsp.csr_matrix((massD, massI, massP), +# shape = ((N - 1) * n, (N - 1) * n)) +# return stiff, mass +# +#def eigNonlinearSparse(As:List[Np2D], jSupp : int = 1, +# return_inverse : bool = False, +# **kwargs_eig) -> Tuple[Np1D, Np2D]: +# stiff, mass = linearizeSparse(As, jSupp) +# ev, P = scsp.linalg.eig(stiff, M = mass, return_eigenvectors = True, +# **kwargs_eig) +# if not return_inverse: return ev, P +# Pinv = customPInv(P) +# return ev, P, Pinv +# +#def eigvalsNonlinearSparse(As:List[Np2D], jSupp : int = 1, +# **kwargs_eigvals) -> Np1D: +# stiff, mass = linearizeSparse(As, jSupp) +# return scsp.linalg.eig(stiff, M = mass, return_eigenvectors = False, +# **kwargs_eigvals) diff --git a/rrompy/utilities/base/number_theory.py b/rrompy/utilities/numerical/number_theory.py similarity index 96% rename from rrompy/utilities/base/number_theory.py rename to rrompy/utilities/numerical/number_theory.py index c71c9df..cc728ab 100644 --- a/rrompy/utilities/base/number_theory.py +++ b/rrompy/utilities/numerical/number_theory.py @@ -1,70 +1,70 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np -__all__ = ['squareResonances','getLowestPrimeFactor','primeFactorize'] +__all__ = ['squareResonances'] prime_v = [] def squareResonances(a:int, b:int, zero_terms : bool = True): spectrum = [] a = max(int(np.floor(a)), 0) b = max(int(np.ceil(b)), 0) global prime_v if len(prime_v) == 0: prime_v = [2, 3] if a > prime_v[-1]: for i in range(prime_v[-1], a, 2): getLowestPrimeFactor(i) for i in range(a, b + 1): spectrum = spectrum + [i] * countSquareSums(i, zero_terms) return np.array(spectrum) def getLowestPrimeFactor(n:int): global prime_v for x in prime_v: if n % x == 0: return x if prime_v[-1] < n: prime_v = prime_v + [n] return n def primeFactorize(n:int): factors = [] number = n while number > 1: factor = getLowestPrimeFactor(number) factors.append(factor) number = int(number / factor) if n < -1: factors[0] = -factors[0] return list(factors) def countSquareSums(n:int, zero_terms : bool = True): if n < 2: return (n + 1) * zero_terms factors = primeFactorize(n) funique, fcounts = np.unique(factors, return_counts = True) count = 1 for fac, con in zip(funique, fcounts): #using number theory magic if fac % 4 == 1: count = count * (con + 1) elif fac % 4 == 3 and con % 2 == 1: return 0 return count + (2 * zero_terms - 1) * (int(n ** .5) ** 2 == n) diff --git a/rrompy/utilities/numerical/point_matching.py b/rrompy/utilities/numerical/point_matching.py new file mode 100644 index 0000000..9029e02 --- /dev/null +++ b/rrompy/utilities/numerical/point_matching.py @@ -0,0 +1,165 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from itertools import permutations +from rrompy.utilities.base.types import Np1D, Np2D +from rrompy.utilities.base import verbosityManager as vbMng +from rrompy.utilities.exception_manager import RROMPyException + +__all__ = ['pointMatching'] + +def matchExplicit(distanceMatrix:Np2D, idx1:Np1D, idx2:Np1D, + verbObj = None) -> Np1D: + if verbObj is not None: + vbMng(verbObj, "INIT", ("Starting exact point matching of {} " + "points.").format(len(distanceMatrix)), 25) + L1 = len(idx1) + matches, optV = None, None + idxLead = 0 + for idx2p in permutations(idx2, L1): + if verbObj is not None and idxLead != idx2p[0]: + idxLead += 1 + explRatio = 100 * idxLead / len(idx2) + vbMng(verbObj, "MAIN", + "Explored {}% of permutations.".format(int(explRatio)), 65) + val = np.sum(distanceMatrix[idx1, idx2p]) + if optV is None or val < optV: + optV, matches = val, idx2p + if verbObj is not None: + vbMng(verbObj, "DEL", "Done point matching.", 25) + return np.array(matches) + +def findClusterBipartiteRecursive(i, edges, clusters, explored, right): + if explored[right][i] == 0: return clusters, explored + explored[right][i] = 0 + if edges[right][i] not in clusters[1 - right]: + clusters[1 - right] += [edges[right][i]] + clusters, explored = findClusterBipartiteRecursive(edges[right][i], edges, + clusters, explored, + 1 - right) + for k, i2 in enumerate(edges[1 - right]): + if i2 == i and k not in clusters[1 - right]: + clusters[1 - right] += [k] + clusters, explored = findClusterBipartiteRecursive(k, edges, + clusters, explored, + 1 - right) + return clusters, explored + +def pointMatchingHeuristic(distanceMatrix:Np2D, idx1:Np1D, idx2:Np1D, + max_iter : int = 10, verbObj = None, + expl_threshold : int = 8) -> Np1D: + if verbObj is not None: + vbMng(verbObj, "INIT", ("Starting heuristic point matching of {} " + "points.").format(len(idx1)), 25) + distanceMatrixEff = distanceMatrix[idx1, :] + distanceMatrixEff = distanceMatrixEff[:, idx2] + N = len(distanceMatrixEff) + matches = - np.ones(N, dtype = int) + fBest = np.argmin(distanceMatrixEff, axis = 1) + bBest = np.argmin(distanceMatrixEff, axis = 0) + clusters1, clusters2 = [], [] + fIdxs = [np.ones(N, dtype = bool), np.ones(N, dtype = bool)] + if verbObj is not None: vbMng(verbObj, "INIT", "Starting clustering.", 65) + for i in range(N): + if fIdxs[0][i]: + cloc, fIdxs = findClusterBipartiteRecursive(i, [fBest, bBest], + [[], []], fIdxs, 0) + clusters1 += [cloc[0]] + clusters2 += [cloc[1]] + if verbObj is not None: vbMng(verbObj, "DEL", "Done clustering.", 65) + if verbObj is not None: + vbMng(verbObj, "INIT", "Starting optimization of clustered points.", + 65) + for c1, c2 in zip(clusters1, clusters2): + if len(c1) > len(c2): + if len(c1) > expl_threshold: + optP = np.random.permutation(c1)[: len(c2)] + max_iter += int(np.ceil(np.log2(len(c1)))) + else: + optP = matchExplicit(distanceMatrixEff.T, c2, c1) + for i, k in enumerate(optP): + matches[k] = c2[i] + else: + if len(c2) > expl_threshold: + optP = np.random.permutation(c2)[: len(c1)] + max_iter += int(np.ceil(np.log2(len(c2)))) + else: + optP = matchExplicit(distanceMatrixEff, c1, c2) + for i, k in enumerate(c1): + matches[k] = optP[i] + front1 = np.where(matches == -1)[0] + if len(front1) == 0: + if verbObj is not None: vbMng(verbObj, "DEL", "Done optimizing.", 65) + if verbObj is not None: + vbMng(verbObj, "DEL", "Done point matching.", 25) + return idx2[matches] + if len(front1) == N: + raise RROMPyException(("Heuristic point matching algorithm not " + "converging. Must increase threshold.")) + optP = np.array([i for i in range(N) if i not in matches], dtype = int) + if verbObj is not None: vbMng(verbObj, "DEL", "Done optimizing.", 65) + for _ in range(max_iter): + bulk1 = [i for i in range(N) if i not in front1] + if verbObj is not None: + vbMng(verbObj, "INIT", ("Starting optimization of {} unclustered " + "points.").format(len(front1)), 65) + optP = pointMatching(distanceMatrixEff, idx1 = front1, idx2 = optP) + for i, k in enumerate(front1): matches[k] = optP[i] + if verbObj is not None: vbMng(verbObj, "DEL", "Done optimizing.", 65) + if verbObj is not None: + vbMng(verbObj, "INIT", + ("Starting optimization of {}x{} frontier " + "points.").format(len(front1), len(bulk1)), 65) + keepfront1, addfront1, addoptP = [], [], [] + for i, il1 in enumerate(front1): + il2 = optP[i] + change = False + for ib1 in bulk1: + ib2 = matches[ib1] + val = ( + distanceMatrixEff[il1, ib2] + distanceMatrixEff[ib1, il2] + - distanceMatrixEff[il1, il2] - distanceMatrixEff[ib1, ib2]) + if val < 0: + addfront1 += [ib1] + addoptP += [il2] + matches[ib1] = il2 + matches[il1] = ib2 + il2 = ib2 + optP[i] = ib2 + change = True + if change: keepfront1 += [i] + front1, optP = front1[keepfront1], optP[keepfront1] + for (addb, addo) in zip(addfront1[::-1], addoptP[::-1]): + if addb not in front1: + front1, optP = np.append(front1, addb), np.append(optP, addo) + if verbObj is not None: vbMng(verbObj, "DEL", "Done optimizing.", 65) + if len(front1) == 0: break + if verbObj is not None: vbMng(verbObj, "DEL", "Done point matching.", 25) + return idx2[matches] + +def pointMatching(distanceMatrix:Np2D, expl_threshold : int = 8, + max_iter : int = 10, verbObj = None, idx1 : Np1D = None, + idx2 : Np1D = None) -> Np1D: + N = len(distanceMatrix) + if idx1 is None: idx1 = np.arange(N) + if idx2 is None: idx2 = np.arange(N) + if len(idx1) > expl_threshold: + return pointMatchingHeuristic(distanceMatrix, idx1, idx2, max_iter, + verbObj, expl_threshold) + return matchExplicit(distanceMatrix, idx1, idx2, verbObj) diff --git a/rrompy/utilities/base/sobol.py b/rrompy/utilities/numerical/sobol.py similarity index 100% rename from rrompy/utilities/base/sobol.py rename to rrompy/utilities/numerical/sobol.py diff --git a/rrompy/utilities/poly_fitting/__init__.py b/rrompy/utilities/poly_fitting/__init__.py index c9d4457..7655844 100644 --- a/rrompy/utilities/poly_fitting/__init__.py +++ b/rrompy/utilities/poly_fitting/__init__.py @@ -1,27 +1,27 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from .custom_fit import customFit -from .custom_pinv import customPInv +from .interpolator import GenericInterpolator __all__ = [ 'customFit', - 'customPInv' + 'GenericInterpolator' ] diff --git a/rrompy/utilities/poly_fitting/radial_basis/__init__.py b/rrompy/utilities/poly_fitting/heaviside/__init__.py similarity index 66% copy from rrompy/utilities/poly_fitting/radial_basis/__init__.py copy to rrompy/utilities/poly_fitting/heaviside/__init__.py index e4d799d..4074d47 100644 --- a/rrompy/utilities/poly_fitting/radial_basis/__init__.py +++ b/rrompy/utilities/poly_fitting/heaviside/__init__.py @@ -1,42 +1,41 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -from .kernel import radialGaussian, thinPlateSpline, multiQuadric -from .base import rbbases, polybases, polyfitname, polydomcoeff, radialFunction -from .der import polyder +from .base import polybases, polyfitname, polydomcoeff from .val import polyval -from .vander import rbvander, polyvander +from .vander import polyvander from .homogeneization import homogeneizedpolyvander +from .heaviside_to_from_rational import heaviside2rational, rational2heaviside +from .heaviside_to_from_affine import heaviside2affine, affine2heaviside +from .heaviside_interpolator import HeavisideInterpolator __all__ = [ - 'radialGaussian', - 'thinPlateSpline', - 'multiQuadric', - 'rbbases', 'polybases', 'polyfitname', 'polydomcoeff', - 'radialFunction', - 'polyder', 'polyval', - 'rbvander', 'polyvander', - 'homogeneizedpolyvander' + 'heaviside2rational', + 'rational2heaviside', + 'heaviside2affine', + 'affine2heaviside', + 'homogeneizedpolyvander', + 'HeavisideInterpolator' ] diff --git a/rrompy/utilities/poly_fitting/radial_basis/der.py b/rrompy/utilities/poly_fitting/heaviside/base.py similarity index 55% rename from rrompy/utilities/poly_fitting/radial_basis/der.py rename to rrompy/utilities/poly_fitting/heaviside/base.py index 89f2aed..cb19d09 100644 --- a/rrompy/utilities/poly_fitting/radial_basis/der.py +++ b/rrompy/utilities/poly_fitting/heaviside/base.py @@ -1,30 +1,36 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -import numpy as np -from rrompy.utilities.base.types import Np1D, List, radialFun from rrompy.utilities.exception_manager import RROMPyException +from rrompy.utilities.poly_fitting.polynomial.base import (polybases as pbP, + polyfitname as pfnP, + polydomcoeff as polydomcoeffB) -__all__ = ['polyder'] +__all__ = ['polybases', 'polyfitname', 'polydomcoeff'] -def polyder(c:radialFun, basis:str, m : List[int] = None, - scl : Np1D = None) -> radialFun: - if m is not None and np.sum(m) > 0: - raise RROMPyException(("Cannot take derivatives of radial basis " - "function.")) - return c +polybases = [x + "_HEAVISIDE" for x in pbP] + +def polyfitname(basis:str) -> str: + basisp = basis.split("_")[0] + try: + return pfnP(basisp) + "_heaviside" + except: + raise RROMPyException("Polynomial-Heaviside basis not recognized.") + +def polydomcoeff(n:int, basis:str) -> float: + return polydomcoeffB(n, basis.split("_")[0]) diff --git a/rrompy/utilities/poly_fitting/heaviside/heaviside_interpolator.py b/rrompy/utilities/poly_fitting/heaviside/heaviside_interpolator.py new file mode 100644 index 0000000..f0aca96 --- /dev/null +++ b/rrompy/utilities/poly_fitting/heaviside/heaviside_interpolator.py @@ -0,0 +1,77 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from copy import deepcopy as copy +from rrompy.utilities.base.types import (List, ListAny, Np1D, paramList, + interpEng) +from rrompy.utilities.base import freepar as fp +from rrompy.utilities.poly_fitting.polynomial.polynomial_interpolator import ( + PolynomialInterpolator) +from rrompy.utilities.poly_fitting.polynomial.roots import polyroots +from rrompy.utilities.poly_fitting.heaviside.val import polyval +from rrompy.utilities.poly_fitting.heaviside.heaviside_to_from_rational import\ + heaviside2rational, rational2heaviside +from rrompy.utilities.exception_manager import RROMPyAssert + +__all__ = ['HeavisideInterpolator'] + +class HeavisideInterpolator(PolynomialInterpolator): + """HERE""" + + def __init__(self, other = None): + if other is None: return + self.poles = other.poles + super().__init__(other) + + def __call__(self, mu:paramList, der : List[int] = None, + scl : Np1D = None): + return polyval(mu, self.coeffs, self.poles, self.polybasis) + + def __copy__(self): + return HeavisideInterpolator(self) + + def __deepcopy__(self, memo): + other = HeavisideInterpolator() + other.poles, other.coeffs, other.npar, other.polybasis = copy( + (self.poles, self.coeffs, self.npar, self.polybasis), memo) + return other + + def pad(self, nleft : List[int] = None, nright : List[int] = None): + if nleft is None: nleft = [0] * len(self.shape) + if nright is None: nright = [0] * len(self.shape) + if not hasattr(nleft, "__len__"): nleft = [nleft] + if not hasattr(nright, "__len__"): nright = [nright] + RROMPyAssert(nleft[0], 0, "Padding in free direction") + super().pad(nleft, nright) + + def setupFromRational(self, num:interpEng, den:interpEng, + murange : Np1D = np.array([-1., 1.]), + scl : Np1D = None, scalingExp : List[float] = None): + self.coeffs, self.poles, self.polybasis = rational2heaviside(num, den, + murange, scl, + scalingExp) + + def roots(self, marginalVals : ListAny = [fp], murange : Np1D = None, + scalingExp : List[float] = None): + RROMPyAssert(self.shape, (1,), "Shape of output") + RROMPyAssert(marginalVals, [fp], "Marginal values") + basisN = self.polybasis.split("_")[0] + coeffsN = heaviside2rational(self.coeffs, self.poles, murange, basisN, + scalingExp = scalingExp)[0] + return polyroots(coeffsN, basisN) diff --git a/rrompy/utilities/poly_fitting/heaviside/heaviside_to_from_affine.py b/rrompy/utilities/poly_fitting/heaviside/heaviside_to_from_affine.py new file mode 100644 index 0000000..11cbc3b --- /dev/null +++ b/rrompy/utilities/poly_fitting/heaviside/heaviside_to_from_affine.py @@ -0,0 +1,94 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from scipy.special import binom +import scipy.sparse as sp +from rrompy.utilities.base.types import (Np1D, Np2D, List, ListAny, Tuple, + paramVal) +from rrompy.utilities.numerical import eigNonlinearDense +from rrompy.utilities.exception_manager import RROMPyException +from rrompy.parameter import checkParameter + +__all__ = ['heaviside2affine', 'affine2heaviside'] + +def heaviside2affine(c:Np2D, poles:Np1D, mu : paramVal = [], + basis : str = "MONOMIAL_HEAVISIDE", + sparse : bool = False) \ + -> Tuple[Np2D, List[Np2D], List[Np1D]]: + mu = checkParameter(mu, 1)(0, 0) + n, d = len(poles), len(c) - len(poles) + basisN = basis.split("_")[0] + if basisN not in ["MONOMIAL", "CHEBYSHEV", "LEGENDRE"]: + raise RROMPyException("Polynomial basis not recognized.") + if sparse: + A0 = sp.spdiags([np.concatenate((- mu - poles, np.ones(d)))], + [0], n + d, n + d) + A1 = sp.spdiags([np.concatenate((np.ones(n), np.zeros(d)))], + [0], n + d, n + d) + else: + A0 = np.diag(np.concatenate((mu - poles, np.ones(d)))) + A1 = np.diag(np.concatenate((np.ones(n), np.zeros(d)))) + As = [A0, A1] + bs = np.zeros((d, n + d), dtype = poles.dtype) + bs[0, :] = 1. + if d > 0: + bs[0, n + 1 :] = 0. + if d > 1: + bs[1, n + 1] = 1. + for j in range(2, d): + if basisN == "MONOMIAL": + bs[j, n + j] = 1. + else: + alpha = - 1. if basisN == "CHEBYSHEV" else 1. / j - 1. + bs[:, n + j] = alpha * bs[:, n + j - 2] + bs[1 :, n + j] += (1. - alpha) * bs[: -1, n + j - 1] + bs = list(bs) + return c.reshape(c.shape[0], -1).T, As, bs + +def affine2heaviside(As:ListAny, bs:ListAny, + jSupp : int = 1) -> Tuple[Np2D, Np1D, str]: + if jSupp != 1 and not (isinstance(jSupp, (int,)) + and jSupp.upper() == "COMPANION"): + raise RROMPyException(("Affine to heaviside conversion does not allow " + "nonlinear eigenproblem support outside first " + "block row.")) + N = len(As) + M = len(bs) + n = As[0].shape[0] + if N == 1: + poles = np.empty(0, dtype = np.complex) + Q = np.eye(n) + else: + basis = "MONOMIAL_HEAVISIDE" + poles, P, Q = eigNonlinearDense(As, jSupp = jSupp, + return_inverse = True) + P = P[- n :, :] + Q = Q[:, : n] + bEffs = np.array([Q.dot(np.linalg.solve(As[-1], b)) for b in bs]) + if N == 1: + c = bEffs + else: + c = np.zeros((len(poles) + M - 1, As[0].shape[1]), dtype = As[0].dtype) + for l, pl in enumerate(poles): + for i in range(M): + c[l, :] = pl ** i * bEffs[i, l] * P[:, l] + for l in range(M - 1): + for i in range(l + 1, M): + c[len(poles) + l, :] = P.dot(poles ** (i- 1 - l) * bEffs[i, :]) + return c, poles, basis diff --git a/rrompy/utilities/poly_fitting/heaviside/heaviside_to_from_rational.py b/rrompy/utilities/poly_fitting/heaviside/heaviside_to_from_rational.py new file mode 100644 index 0000000..89f7e3d --- /dev/null +++ b/rrompy/utilities/poly_fitting/heaviside/heaviside_to_from_rational.py @@ -0,0 +1,97 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from rrompy.utilities.poly_fitting.polynomial.polynomial_interpolator import \ + PolynomialInterpolator +from rrompy.utilities.poly_fitting.polynomial.vander import polyvander +from rrompy.utilities.poly_fitting.custom_fit import customFit +from rrompy.utilities.base.types import Np1D, Np2D, List, Tuple, interpEng +from rrompy.parameter.parameter_sampling import (RandomSampler as RS, + QuadratureSampler as QS) +from .val import polyval +from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert + +__all__ = ['heaviside2rational', 'rational2heaviside'] + +def heaviside2rational(c:Np2D, poles:Np1D, murange : Np1D = None, + basis : str = "MONOMIAL_HEAVISIDE", basisD : str = None, + scalingExp : List[float] = None) \ + -> Tuple[interpEng, interpEng]: + num = PolynomialInterpolator() + den = PolynomialInterpolator() + basisN = basis.split("_")[0] + if basisD is None: basisD = basisN + if murange is None: + multiplier = [1., 1.j] + avgs = [np.mean(np.real(poles)), np.mean(np.imag(poles))] + ranges = np.array([[np.min(np.real(poles)), np.max(np.real(poles))], + [np.min(np.imag(poles)), np.max(np.imag(poles))]]) + domIdx = np.argmax(ranges[:, 1] - ranges[:, 0]) + murange = [multiplier[domIdx] * x + + multiplier[1 - domIdx] * avgs[1 - domIdx] + for x in ranges[domIdx, :]] + extraPt = None + while extraPt is None or np.any(np.isclose(extraPt, poles)): + extraPt = murange[0] + (murange[1] - murange[0]) * np.random.rand(1) + denAuxPts = np.concatenate((poles, extraPt)) + denAuxVals = np.concatenate((np.zeros(len(poles)), [1.])) + den.setupByInterpolation(denAuxPts, denAuxVals, len(poles), basisD) + den.coeffs /= np.linalg.norm(den.coeffs) + if basis == "CHEBYSHEV": + sampler = QS(murange, "CHEBYSHEV", scalingExp) + elif basis == "LEGENDRE": + sampler = QS(murange, "GAUSSLEGENDRE", scalingExp) + else: + sampler = RS(murange, "HALTON", scalingExp) + xAux = sampler.generatePoints(len(c)) + valsAux = den(xAux) * polyval(xAux, c, poles, basis) + num.setupByInterpolation(xAux, valsAux, len(c) - 1, basisN) + return num, den + +def rational2heaviside(num:interpEng, den:interpEng, + murange : Np1D = np.array([-1., 1.]), scl : Np1D = None, + scalingExp : List[float] = None) \ + -> Tuple[Np2D, Np1D, str]: + if (not isinstance(num, PolynomialInterpolator) + or not isinstance(den, PolynomialInterpolator)): + raise RROMPyException(("Rational numerator and denominator must be " + "polynomial interpolators.")) + RROMPyAssert(num.npar, 1, "Number of parameters") + RROMPyAssert(den.npar, 1, "Number of parameters") + basis = num.polybasis + "_HEAVISIDE" + c = np.zeros_like(num.coeffs) + poles = den.roots() + Psp = num(poles) + Qspder = den(poles, 1, scl) + c[: len(poles)] = (Psp / Qspder).T + if len(c) > len(poles): + from rrompy.parameter.parameter_sampling import (RandomSampler as RS, + QuadratureSampler as QS) + if num.polybasis == "CHEBYSHEV": + sampler = QS(murange, "CHEBYSHEV", scalingExp) + elif num.polybasis == "LEGENDRE": + sampler = QS(murange, "GAUSSLEGENDRE", scalingExp) + else: + sampler = RS(murange, "HALTON", scalingExp) + xAux = sampler.generatePoints(len(c)) + valsAux = (num(xAux) / den(xAux) + - polyval(xAux, c, poles, basis)).T + VanAux = polyvander(xAux, [len(c) - len(poles) - 1], num.polybasis) + c[len(poles) :] = customFit(VanAux, valsAux) + return c, poles, basis diff --git a/rrompy/utilities/poly_fitting/heaviside/homogeneization.py b/rrompy/utilities/poly_fitting/heaviside/homogeneization.py new file mode 100644 index 0000000..d72c308 --- /dev/null +++ b/rrompy/utilities/poly_fitting/heaviside/homogeneization.py @@ -0,0 +1,51 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, paramList +from rrompy.utilities.poly_fitting.polynomial.homogeneization import ( + homogeneizedpolyvander as hpvP) +from rrompy.utilities.exception_manager import RROMPyException +from .vander import heavisidevander + +__all__ = ['homogeneizedpolyvander'] + +def homogeneizedpolyvander(x:paramList, poles:Np1D, deg : int = None, + basis : str = "MONOMIAL_HEAVISIDE", + derIdxs : List[List[List[int]]] = None, + reorder : List[int] = None, scl : Np1D = None)\ + -> Tuple[Np2D, List[List[int]], List[int]]: + """ + Compute radial-basis-inclusive homogeneized Hermite-Vandermonde matrix with + specified derivative directions. + """ + if derIdxs is not None and np.sum(np.sum(derIdxs)) > 0: + raise RROMPyException(("Cannot take derivatives of radial basis " + "function.")) + basisp = basis.split("_")[0] + VanR = heavisidevander(x, poles, reorder = reorder) + if deg is None or deg < 0: + VanP = np.empty((len(x), 0)) + derIdxs, ordIdxs = np.zeros(0, dtype = int), np.zeros(0, dtype = int) + else: + VanP, derIdxs, ordIdxs = hpvP(x, deg, basisp, derIdxs = derIdxs, + reorder = reorder, scl = scl) + ordIdxsEff = np.concatenate((np.arange(len(VanR)), ordIdxs + len(VanR))) + return (np.block([[VanR, VanP], + [VanP.T.conj(), np.zeros(tuple([VanP.shape[1]] * 2))]]), + derIdxs, ordIdxsEff) diff --git a/rrompy/utilities/poly_fitting/heaviside/val.py b/rrompy/utilities/poly_fitting/heaviside/val.py new file mode 100644 index 0000000..2a8fa45 --- /dev/null +++ b/rrompy/utilities/poly_fitting/heaviside/val.py @@ -0,0 +1,46 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from copy import deepcopy as copy +from rrompy.utilities.base.types import Np1D, Np2D, paramList +from rrompy.parameter import checkParameterList +from rrompy.utilities.poly_fitting.polynomial import polyval as pvP + +__all__ = ['polyval'] + +def polyval(x:paramList, c:Np2D, poles:Np1D, + basis : str = "MONOMIAL_HEAVISIDE") -> Np2D: + x = checkParameterList(x, 1)[0] + poles = poles.flatten() + if len(c) > len(poles): + basisp = basis.split("_")[0] + c0 = pvP(x, c[len(poles) :], basisp) + else: + c0 = np.zeros(c.shape[1:] + (len(x),), dtype = c.dtype) + csh = copy(c0.shape) + if len(csh) == 1: c0 = c0.reshape(1, -1) + for j in range(len(x)): + muDiff = 1. / (x[j] - poles) + val = muDiff.dot(c[: len(poles)]) + try: + c0[..., j] += val + except: + c0[..., j] += val.flatten() + if len(csh) == 1: c0 = c0.flatten() + return c0 diff --git a/rrompy/utilities/poly_fitting/heaviside/vander.py b/rrompy/utilities/poly_fitting/heaviside/vander.py new file mode 100644 index 0000000..1b752de --- /dev/null +++ b/rrompy/utilities/poly_fitting/heaviside/vander.py @@ -0,0 +1,62 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from rrompy.utilities.poly_fitting.polynomial.vander import polyvander as pvP +from rrompy.utilities.base.types import Np1D, Np2D, List, paramList +from rrompy.parameter import checkParameterList +from rrompy.utilities.exception_manager import RROMPyException + +__all__ = ['heavisidevander', 'polyvander'] + +def heavisidevander(x:paramList, poles:Np1D, + reorder : List[int] = None) -> Np2D: + """Compute Heaviside-Vandermonde matrix.""" + x = checkParameterList(x, 1)[0] + x_un, idx_un = x.unique(return_inverse = True) + nx = len(x) + if len(x_un) < nx: + raise RROMPyException("Sample points must be distinct.") + del x_un + x = x.data.flatten() + if reorder is not None: x = x[reorder] + poles = poles.flatten() + Van = np.empty((len(x), len(poles)), dtype = poles.dtype) + for j in range(len(x)): + Van[j, :] = 1. / (x[j] - poles) + return Van + +def polyvander(x:paramList, poles:Np1D, degs : List[int] = None, + basis : str = "MONOMIAL_HEAVISIDE", + derIdxs : List[List[List[int]]] = None, + reorder : List[int] = None, scl : Np1D = None) -> Np2D: + """ + Compute Heaviside-inclusive Hermite-Vandermonde matrix with specified + derivative directions. + """ + if derIdxs is not None and np.sum(np.sum(derIdxs)) > 0: + raise RROMPyException(("Cannot take derivatives of heaviside " + "function.")) + basisp = basis.split("_")[0] + VanH = heavisidevander(x, poles, reorder = reorder) + if degs is None or np.sum(degs) < 0: + VanP = np.empty((len(x), 0)) + else: + VanP = pvP(x, degs, basisp, derIdxs = derIdxs, reorder = reorder, + scl = scl) + return np.block([[VanH, VanP]]) diff --git a/rrompy/hfengines/base/__init__.py b/rrompy/utilities/poly_fitting/interpolator.py similarity index 61% copy from rrompy/hfengines/base/__init__.py copy to rrompy/utilities/poly_fitting/interpolator.py index 5046f4a..fd08cd3 100644 --- a/rrompy/hfengines/base/__init__.py +++ b/rrompy/utilities/poly_fitting/interpolator.py @@ -1,32 +1,41 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -from .matrix_engine_base import MatrixEngineBase -from .problem_engine_base import ProblemEngineBase -from .vector_problem_engine_base import VectorProblemEngineBase -from .boundary_conditions import BoundaryConditions +from abc import abstractmethod +from rrompy.utilities.base.types import List, paramList -__all__ = [ - 'MatrixEngineBase', - 'ProblemEngineBase', - 'VectorProblemEngineBase', - 'BoundaryConditions' - ] +__all__ = ['GenericInterpolator'] +class GenericInterpolator: + """HERE""" + @abstractmethod + def __init__(self, other = None): + pass + @abstractmethod + def __call__(self, mu:paramList, der : List[int] = None): + pass + + @abstractmethod + def __copy__(self): + pass + + @abstractmethod + def __deepcopy__(self, memo): + pass diff --git a/rrompy/utilities/poly_fitting/polynomial/__init__.py b/rrompy/utilities/poly_fitting/polynomial/__init__.py index 5f6eade..747c8b5 100644 --- a/rrompy/utilities/poly_fitting/polynomial/__init__.py +++ b/rrompy/utilities/poly_fitting/polynomial/__init__.py @@ -1,45 +1,49 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from .base import (polybases, polyfitname, polydomcoeff) from .der import polyder from .val import polyval +from .marginalize import polymarginalize from .vander import polyvander from .roots import polyroots from .derivative import nextDerivativeIndices from .hash_derivative import hashDerivativeToIdx, hashIdxToDerivative from .homogeneization import (homogeneizationMask, homogeneizedpolyvander, homogeneizedToFull) +from .polynomial_interpolator import PolynomialInterpolator __all__ = [ 'polybases', 'polyfitname', 'polydomcoeff', 'polyder', 'polyval', + 'polymarginalize', 'polyvander', 'polyroots', 'nextDerivativeIndices', 'hashDerivativeToIdx', 'hashIdxToDerivative', 'homogeneizationMask', 'homogeneizedpolyvander', - 'homogeneizedToFull' + 'homogeneizedToFull', + 'PolynomialInterpolator' ] diff --git a/rrompy/utilities/poly_fitting/polynomial/homogeneization.py b/rrompy/utilities/poly_fitting/polynomial/homogeneization.py index b4fbb6f..97fa102 100644 --- a/rrompy/utilities/poly_fitting/polynomial/homogeneization.py +++ b/rrompy/utilities/poly_fitting/polynomial/homogeneization.py @@ -1,62 +1,62 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, paramList from rrompy.utilities.poly_fitting.polynomial import (polyvander, hashIdxToDerivative as hashI) from rrompy.parameter import checkParameterList __all__ = ['homogeneizationMask', 'homogeneizedpolyvander', 'homogeneizedToFull'] def homogeneizationMask(degs:List[int]) -> Tuple[List[int], List[bool]]: dim = len(degs) N = np.prod([d + 1 for d in degs]) maskN = np.arange(N, dtype = int) remN = np.zeros((N, dim), dtype = int) for j in range(dim - 1, -1, -1): remN[:, j] = maskN % (degs[j] + 1) maskN //= degs[j] + 1 mask = np.sum(remN, axis = 1) <= min(degs) return remN[mask], mask def homogeneizedpolyvander(x:paramList, deg:int, basis:str, derIdxs : List[List[List[int]]] = None, reorder : List[int] = None, scl : Np1D = None)\ -> Tuple[Np2D, List[List[int]], List[int]]: - x, _ = checkParameterList(x) + x = checkParameterList(x)[0] degs = [deg] * x.shape[1] VanBase = polyvander(x, degs, basis, derIdxs, reorder, scl) derIdxs, mask = homogeneizationMask(degs) ordIdxs = np.empty(len(derIdxs), dtype = int) derTotal = np.array([np.sum(y) for y in derIdxs]) idxPrev = 0 rangeAux = np.arange(len(derIdxs)) for j in range(deg + 1): idxLocal = rangeAux[derTotal == j][::-1] idxPrev += len(idxLocal) ordIdxs[idxPrev - len(idxLocal) : idxPrev] = idxLocal return VanBase[:, mask], derIdxs, ordIdxs def homogeneizedToFull(shapeFull:Tuple[int], dim:int, coeffs:Np2D) -> Np2D: full = np.zeros(shapeFull, dtype = coeffs.dtype) for j in range(len(coeffs)): full[tuple(hashI(j, dim))] = coeffs[j] - return full \ No newline at end of file + return full diff --git a/rrompy/utilities/poly_fitting/polynomial/marginalize.py b/rrompy/utilities/poly_fitting/polynomial/marginalize.py new file mode 100644 index 0000000..67ce917 --- /dev/null +++ b/rrompy/utilities/poly_fitting/polynomial/marginalize.py @@ -0,0 +1,58 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +from numpy import array, polynomial as po +from copy import deepcopy as copy +from rrompy.utilities.base.types import Np1D, Np2D +from rrompy.utilities.base import freepar as fp +from rrompy.utilities.exception_manager import RROMPyAssert, RROMPyException + +__all__ = ['polymarginalize'] + +def polymarginalize(c:Np2D, basis:str, marginalVals : Np1D = [fp], + nMarginal : int = None) -> Np1D: + if not hasattr(c, "ndim"): c = array(c) + ndim = c.ndim + if not hasattr(marginalVals, "__len__"): marginalVals = [marginalVals] + marginalVals = list(marginalVals) + try: + polyvalbase = {"CHEBYSHEV" : po.chebyshev.chebval, + "LEGENDRE" : po.legendre.legval, + "MONOMIAL" : po.polynomial.polyval}[basis.upper()] + except: + raise RROMPyException("Polynomial basis not recognized.") + RROMPyAssert(ndim, len(marginalVals), "Marginalized variables") + marginalDims = [] + for j in range(len(marginalVals)): + if marginalVals[j] == fp: + marginalDims += [c.shape[j]] + if nMarginal is not None and len(marginalDims) != nMarginal: + raise RROMPyException(("Exactly {} 'freepar' entries in marginalVals " + "must be provided.").format(nMarginal)) + cEff = [copy(c)] + for d in range(ndim): + if marginalVals[d] != fp: + for dj in range(len(cEff)): + cEff[dj] = polyvalbase(marginalVals[d], cEff[dj], + tensor = False) + else: + cEff2 = [] + for dj in range(len(cEff)): + cEff2 += list(cEff[dj]) + cEff = copy(cEff2) + return array(cEff).reshape(tuple(marginalDims)) diff --git a/rrompy/utilities/poly_fitting/polynomial/polynomial_interpolator.py b/rrompy/utilities/poly_fitting/polynomial/polynomial_interpolator.py new file mode 100644 index 0000000..90f8472 --- /dev/null +++ b/rrompy/utilities/poly_fitting/polynomial/polynomial_interpolator.py @@ -0,0 +1,141 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from copy import deepcopy as copy +from rrompy.utilities.base.types import (List, ListAny, DictAny, Np1D, Np2D, + paramList) +from rrompy.utilities.base import freepar as fp +from rrompy.utilities.poly_fitting.interpolator import GenericInterpolator +from rrompy.utilities.poly_fitting.custom_fit import customFit +from rrompy.utilities.poly_fitting.polynomial.base import (polyfitname, + polydomcoeff) +from rrompy.utilities.poly_fitting.polynomial.val import polyval +from rrompy.utilities.poly_fitting.polynomial.roots import polyroots +from rrompy.utilities.poly_fitting.polynomial.homogeneization import ( + homogeneizedpolyvander as hpv, + homogeneizedToFull) +from rrompy.utilities.poly_fitting.polynomial.vander import polyvander as pv +from rrompy.utilities.exception_manager import RROMPyAssert, RROMPyException +from rrompy.parameter import checkParameterList + +__all__ = ['PolynomialInterpolator'] + +class PolynomialInterpolator(GenericInterpolator): + """HERE""" + + def __init__(self, other = None): + if other is None: return + self.coeffs = other.coeffs + self.npar = other.npar + self.polybasis = other.polybasis + + @property + def shape(self): + if self.coeffs.ndim > self.npar: + sh = self.coeffs.shape[self.npar :] + else: sh = tuple([1]) + return sh + + @property + def deg(self): + return [x - 1 for x in self.coeffs.shape[: self.npar]] + + def __getitem__(self, key): + return self.coeffs[key] + + def __call__(self, mu:paramList, der : List[int] = None, + scl : Np1D = None): + return polyval(mu, self.coeffs, self.polybasis, der, scl) + + def __copy__(self): + return PolynomialInterpolator(self) + + def __deepcopy__(self, memo): + other = PolynomialInterpolator() + other.coeffs, other.npar, other.polybasis = copy( + (self.coeffs, self.npar, self.polybasis), memo) + return other + + @property + def domCoeff(self): + RROMPyAssert(self.npar, 1, "Number of parameters") + return polydomcoeff(self.deg, self.polybasis) * self[-1] + + def pad(self, nleft : List[int] = None, nright : List[int] = None): + if nleft is None: nleft = [0] * len(self.shape) + if nright is None: nright = [0] * len(self.shape) + if not hasattr(nleft, "__len__"): nleft = [nleft] + if not hasattr(nright, "__len__"): nright = [nright] + RROMPyAssert(len(self.shape), len(nleft), "Shape of output") + RROMPyAssert(len(self.shape), len(nright), "Shape of output") + padwidth = [(0, 0)] * self.npar + padwidth = padwidth + [(l, r) for l, r in zip(nleft, nright)] + self.coeffs = np.pad(self.coeffs, padwidth, "constant", + constant_values = (0., 0.)) + + def postmultiplyTensorize(self, A:Np2D): + RROMPyAssert(A.shape[0], self.shape[-1], "Shape of output") + self.coeffs = np.tensordot(self.coeffs, A, axes = (-1, 0)) + + def setupByInterpolation(self, support:paramList, values:ListAny, + deg:int, polybasis : str = "MONOMIAL", + verbose : bool = True, homogeneized : bool = True, + vanderCoeffs : DictAny = {}, + fitCoeffs : DictAny = {}): + support = checkParameterList(support)[0] + self.npar = support.shape[1] + self.polybasis = polybasis + if homogeneized: + vander, _, reorder = hpv(support, deg = deg, basis = polybasis, + **vanderCoeffs) + vander = vander[:, reorder] + else: + if not hasattr(deg, "__len__"): deg = [deg] * self.npar + vander = pv(support, deg = deg, basis = polybasis, + **vanderCoeffs) + outDim = values.shape[1:] + values = values.reshape(values.shape[0], -1) + fitOut = customFit(vander, values, full = True, **fitCoeffs) + P = fitOut[0] + if verbose: + msg = ("Fitting {} samples with degree {} through {}... " + "Conditioning of LS system: {:.4e}.").format( + len(vander), deg, + polyfitname(self.polybasis), + fitOut[1][2][0] / fitOut[1][2][-1]) + else: msg = None + if homogeneized: + self.coeffs = homogeneizedToFull( + tuple([deg + 1] * self.npar) + outDim, + self.npar, P) + else: + self.coeffs = P.reshape(tuple([d + 1 for d in deg]) + outDim) + return fitOut[1][1] == vander.shape[1], msg + + def roots(self, marginalVals : ListAny = [fp]): + RROMPyAssert(self.shape, (1,), "Shape of output") + RROMPyAssert(len(marginalVals), self.npar, "Number of parameters") + try: + rDim = marginalVals.index(fp) + if rDim < len(marginalVals) - 1 and fp in marginalVals[rDim + 1 :]: + raise + except: + raise RROMPyException(("Exactly 1 'freepar' entry in " + "marginalVals must be provided.")) + return polyroots(self.coeffs, self.polybasis, marginalVals) diff --git a/rrompy/utilities/poly_fitting/polynomial/roots.py b/rrompy/utilities/poly_fitting/polynomial/roots.py index ef48d32..f392eb7 100644 --- a/rrompy/utilities/poly_fitting/polynomial/roots.py +++ b/rrompy/utilities/poly_fitting/polynomial/roots.py @@ -1,32 +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 numpy import polynomial as po -from rrompy.utilities.base.types import Np1D +from rrompy.utilities.base.types import Np1D, Np2D +from rrompy.utilities.base import freepar as fp +from .marginalize import polymarginalize from rrompy.utilities.exception_manager import RROMPyException __all__ = ['polyroots'] -def polyroots(c:Np1D, basis:str) -> Np1D: +def polyroots(c:Np2D, basis:str, marginalVals : Np1D = [fp]) -> Np1D: try: rootsbase = {"CHEBYSHEV" : po.chebyshev.chebroots, "LEGENDRE" : po.legendre.legroots, "MONOMIAL" : po.polynomial.polyroots}[basis.upper()] except: raise RROMPyException("Polynomial basis not recognized.") - return rootsbase(c) + return rootsbase(polymarginalize(c, basis, marginalVals, 1)) diff --git a/rrompy/utilities/poly_fitting/polynomial/val.py b/rrompy/utilities/poly_fitting/polynomial/val.py index 7b29cf8..c54463d 100644 --- a/rrompy/utilities/poly_fitting/polynomial/val.py +++ b/rrompy/utilities/poly_fitting/polynomial/val.py @@ -1,43 +1,43 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.utilities.poly_fitting.polynomial import polyder from rrompy.utilities.base.types import Np1D, Np2D, List, paramList from rrompy.parameter import checkParameterList from rrompy.utilities.exception_manager import RROMPyException __all__ = ['polyval'] def polyval(x:paramList, c:Np2D, basis:str, m : List[int] = None, scl : Np1D = None) -> Np2D: c = polyder(c, basis, m = m, scl = scl) - x, _ = checkParameterList(x) + x = checkParameterList(x)[0] if x.shape[1] > c.ndim: raise RROMPyException("Incompatible parameter number.") try: polyvalbase = {"CHEBYSHEV" : np.polynomial.chebyshev.chebval, "LEGENDRE" : np.polynomial.legendre.legval, "MONOMIAL" : np.polynomial.polynomial.polyval }[basis.upper()] except: raise RROMPyException("Polynomial basis not recognized.") c = polyvalbase(x(0), c, tensor = True) for d in range(1, x.shape[1]): c = polyvalbase(x(d), c, tensor = False) return c diff --git a/rrompy/utilities/poly_fitting/polynomial/vander.py b/rrompy/utilities/poly_fitting/polynomial/vander.py index 5a44487..097b738 100644 --- a/rrompy/utilities/poly_fitting/polynomial/vander.py +++ b/rrompy/utilities/poly_fitting/polynomial/vander.py @@ -1,112 +1,114 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.utilities.poly_fitting.polynomial import polyder from rrompy.utilities.base.types import Np1D, Np2D, List, paramList from rrompy.parameter import checkParameterList from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert __all__ = ['polyvander'] def firstDerTransition(dim:int, TDirac:List[Np2D], basis:str, scl : Np1D = None) -> Np2D: C_m = np.zeros((dim, len(TDirac), len(TDirac)), dtype = float) for j, Tj in enumerate(TDirac): m, om = [0] * dim, [(0, 0)] * dim for idx in range(dim): m[idx], om[idx] = 1, (0, 1) J_der = polyder(Tj, basis, m, scl) C_m[idx, :, j] = np.ravel(np.pad(J_der, mode = "constant", pad_width = om)) m[idx], om[idx] = 0, (0, 0) return C_m def countDerDirections(n:int, base:int, digits:int, idx:int): if digits == 0: return [] dig = n % base return [(idx, dig)] * (dig > 0) + countDerDirections( (n - dig) // base, base, digits - 1, idx + 1) def polyvander(x:paramList, degs:List[int], basis:str, derIdxs : List[List[List[int]]] = None, reorder : List[int] = None, scl : Np1D = None) -> Np2D: """ Compute Hermite-Vandermonde matrix with specified derivative directions. E.g. assume that we want to obtain the Vandermonde matrix for (value, derx, derx2) at x = [0, 0], (value, dery) at x = [1, 0], (dery, derxy) at x = [0, 0], of degree 3 in x and 1 in y, using Chebyshev polynomials. This can be done by polyvander([[0, 0], [1, 0]], # unique sample points [3, 1], # polynomial degree "chebyshev", # polynomial family [ [[0, 0], [1, 0], [2, 0], [0, 1], [1, 1]], # derivative directions at first point [[0, 0], [0, 1]] # derivative directions at second point ], [0, 1, 2, 5, 6, 3, 4] # reorder indices ) """ if not isinstance(degs, (list,tuple,np.ndarray,)): degs = [degs] dim = len(degs) - x, _ = checkParameterList(x, dim) + x = checkParameterList(x, dim)[0] x_un, idx_un = x.unique(return_inverse = True) if len(x_un) < len(x): raise RROMPyException("Sample points must be distinct.") del x_un try: vanderbase = {"CHEBYSHEV" : np.polynomial.chebyshev.chebvander, "LEGENDRE" : np.polynomial.legendre.legvander, "MONOMIAL" : np.polynomial.polynomial.polyvander }[basis.upper()] except: raise RROMPyException("Polynomial basis not recognized.") VanBase = vanderbase(x(0), degs[0]) for j in range(1, dim): - VanBase = VanBase[..., None] * vanderbase(x(j), degs[j])[..., None, :] + VNext = vanderbase(x(j), degs[j]) + for jj in range(j): VNext = np.expand_dims(VNext, 1) + VanBase = VanBase[..., None] * VNext VanBase = VanBase.reshape((len(x), -1)) if derIdxs is None or VanBase.shape[-1] <= 1: Van = VanBase else: derFlat, idxRep = [], [] for j, derIdx in enumerate(derIdxs): derFlat += derIdx[:] idxRep += [j] * len(derIdx[:]) for j in range(len(derFlat)): if not hasattr(derFlat[j], "__len__"): derFlat[j] = [derFlat[j]] RROMPyAssert(len(derFlat[j]), dim, "Number of dimensions") TDirac = [y.reshape([d + 1 for d in degs]) for y in np.eye(VanBase.shape[-1], dtype = int)] Cs_loc = firstDerTransition(dim, TDirac, basis, scl) Van = np.empty((len(derFlat), VanBase.shape[-1]), dtype = VanBase.dtype) for j in range(len(derFlat)): Van[j, :] = VanBase[idxRep[j], :] for k in range(dim): for der in range(derFlat[j][k]): Van[j, :] = Van[j, :].dot(Cs_loc[k]) / (der + 1) if reorder is not None: Van = Van[reorder, :] return Van diff --git a/rrompy/utilities/poly_fitting/radial_basis/__init__.py b/rrompy/utilities/poly_fitting/radial_basis/__init__.py index e4d799d..108132b 100644 --- a/rrompy/utilities/poly_fitting/radial_basis/__init__.py +++ b/rrompy/utilities/poly_fitting/radial_basis/__init__.py @@ -1,42 +1,41 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from .kernel import radialGaussian, thinPlateSpline, multiQuadric -from .base import rbbases, polybases, polyfitname, polydomcoeff, radialFunction -from .der import polyder +from .base import rbbases, polybases, polyfitname, polydomcoeff from .val import polyval from .vander import rbvander, polyvander from .homogeneization import homogeneizedpolyvander +from .radial_basis_interpolator import RadialBasisInterpolator __all__ = [ 'radialGaussian', 'thinPlateSpline', 'multiQuadric', 'rbbases', 'polybases', 'polyfitname', 'polydomcoeff', - 'radialFunction', - 'polyder', 'polyval', 'rbvander', 'polyvander', - 'homogeneizedpolyvander' + 'homogeneizedpolyvander', + 'RadialBasisInterpolator' ] diff --git a/rrompy/utilities/poly_fitting/radial_basis/base.py b/rrompy/utilities/poly_fitting/radial_basis/base.py index d5ace7d..a31ebfb 100644 --- a/rrompy/utilities/poly_fitting/radial_basis/base.py +++ b/rrompy/utilities/poly_fitting/radial_basis/base.py @@ -1,55 +1,44 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from itertools import product from rrompy.utilities.base.types import Np1D, Np2D, paramList from rrompy.utilities.exception_manager import RROMPyException -from rrompy.utilities.poly_fitting.polynomial.base import polydomcoeff as \ - polydomcoeffB +from rrompy.utilities.poly_fitting.polynomial.base import (polybases as pbP, + polyfitname as pfnP, + polydomcoeff as polydomcoeffB) -__all__ = ['rbbases', 'polybases', 'polyfitname', 'polydomcoeff', - 'radialFunction'] +__all__ = ['rbbases', 'polybases', 'polyfitname', 'polydomcoeff'] rbbases = ["GAUSSIAN", "THINPLATE", "MULTIQUADRIC"] -polybases = [x + "_" + y for x, y in product( - ["CHEBYSHEV", "LEGENDRE", "MONOMIAL"], rbbases)] +polybases = [x + "_" + y for x, y in product(pbP, rbbases)] def polyfitname(basis:str) -> str: - fitpnames = {"CHEBYSHEV" : "chebfit", "LEGENDRE" : "legfit", - "MONOMIAL" : "polyfit"} fitrnames = {"GAUSSIAN" : "gaussian", "THINPLATE" : "thinplate", "MULTIQUADRIC" : "multiquadric"} basisp, basisr = basis.split("_") try: - return fitpnames[basisp] + "_" + fitrnames[basisr] + return pfnP(basisp) + "_" + fitrnames[basisr] except: raise RROMPyException("Polynomial-radial basis combination not " "recognized.") def polydomcoeff(n:int, basis:str) -> float: return polydomcoeffB(n, basis.split("_")[0]) -class radialFunction: - def __init__(self, supportPoints : paramList = None, - directionalWeights : Np1D = None, localCoeffs : Np1D = None, - globalCoeffs : Np2D = None): - self.supportPoints = supportPoints - self.directionalWeights = directionalWeights - self.localCoeffs = localCoeffs - self.globalCoeffs = globalCoeffs diff --git a/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py b/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py new file mode 100644 index 0000000..cd2584c --- /dev/null +++ b/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py @@ -0,0 +1,142 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from copy import deepcopy as copy +from rrompy.utilities.base.types import (List, ListAny, DictAny, Np1D, Np2D, + paramList) +from rrompy.utilities.poly_fitting.interpolator import GenericInterpolator +from rrompy.utilities.poly_fitting.custom_fit import customFit +from rrompy.utilities.poly_fitting.radial_basis.base import polyfitname +from rrompy.utilities.poly_fitting.radial_basis.val import polyval +from rrompy.utilities.poly_fitting.radial_basis.homogeneization import ( + homogeneizedpolyvander as hpv) +from rrompy.utilities.poly_fitting.radial_basis.vander import polyvander as pv +from rrompy.utilities.poly_fitting.polynomial.homogeneization import ( + homogeneizedToFull) +from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert +from rrompy.parameter import checkParameterList + +__all__ = ['RadialBasisInterpolator'] + +class RadialBasisInterpolator(GenericInterpolator): + """HERE""" + + def __init__(self, other = None): + if other is None: return + self.support = other.support + self.coeffsGlobal = other.coeffsGlobal + self.coeffsLocal = other.coeffsLocal + self.directionalWeights = other.directionalWeights + self.npar = other.npar + self.polybasis = other.polybasis + + @property + def shape(self): + sh = self.coeffsLocal.shape[1 :] if self.coeffsLocal.ndim > 1 else 1 + return sh + + @property + def deg(self): + return [x - 1 for x in self.coeffsGlobal.shape[: self.npar]] + + def __call__(self, mu:paramList, der : List[int] = None, + scl : Np1D = None): + if der is not None and np.sum(der) > 0: + raise RROMPyException(("Cannot take derivatives of radial basis " + "function.")) + return polyval(mu, self.coeffsGlobal, self.coeffsLocal, self.support, + self.directionalWeights, self.polybasis) + + def __copy__(self): + return RadialBasisInterpolator(self) + + def __deepcopy__(self, memo): + other = RadialBasisInterpolator() + (other.support, other.coeffsGlobal, other.coeffsLocal, + other.directionalWeights, other.npar, other.polybasis) = copy( + (self.support, self.coeffsGlobal, self.coeffsLocal, + self.directionalWeights, self.npar, self.polybasis), memo) + return other + + def postmultiplyTensorize(self, A:Np2D): + RROMPyAssert(A.shape[0], self.shape[-1], "Shape of output") + self.coeffsLocal = np.tensordot(self.coeffsLocal, A, axes = (-1, 0)) + self.coeffsGlobal = np.tensordot(self.coeffsGlobal, A, axes = (-1, 0)) + + def pad(self, nleft : List[int] = None, nright : List[int] = None): + if nleft is None: nleft = [0] * len(self.shape) + if nright is None: nright = [0] * len(self.shape) + if not hasattr(nleft, "__len__"): nleft = [nleft] + if not hasattr(nright, "__len__"): nright = [nright] + RROMPyAssert(len(self.shape), len(nleft), "Shape of output") + RROMPyAssert(len(self.shape), len(nright), "Shape of output") + padwidth = [(0, 0)] + [(l, r) for l, r in zip(nleft, nright)] + self.coeffsLocal = np.pad(self.coeffsLocal, padwidth, "constant", + constant_values = (0., 0.)) + padwidth = [(0, 0)] * (self.npar - 1) + padwidth + self.coeffsGlobal = np.pad(self.coeffsGlobal, padwidth, "constant", + constant_values = (0., 0.)) + + def setupByInterpolation(self, support:paramList, values:ListAny, + deg:int, polybasis : str = "MONOMIAL_GAUSSIAN", + directionalWeights : Np1D = None, + verbose : bool = True, homogeneized : bool = True, + vanderCoeffs : DictAny = {}, + fitCoeffs : DictAny = {}): + support = checkParameterList(support)[0] + self.support = copy(support) + if "reorder" in vanderCoeffs.keys(): + self.support = self.support[vanderCoeffs["reorder"]] + self.npar = support.shape[1] + if directionalWeights is None: + directionalWeights = np.ones(self.npar) + self.directionalWeights = directionalWeights + self.polybasis = polybasis + if homogeneized: + vander, _, reorder = hpv(support, deg = deg, basis = polybasis, + directionalWeights = self.directionalWeights, + **vanderCoeffs) + vander = vander[reorder] + vander = vander[:, reorder] + else: + if not hasattr(deg, "__len__"): deg = [deg] * self.npar + vander = pv(support, deg = deg, basis = polybasis, + **vanderCoeffs) + outDim = values.shape[1:] + values = values.reshape(values.shape[0], -1) + values = np.pad(values, ((0, len(vander) - len(values)), (0, 0)), + "constant") + fitOut = customFit(vander, values, full = True, **fitCoeffs) + P = fitOut[0][len(support) :] + if verbose: + msg = ("Fitting {}+{} samples with degree {} through {}... " + "Conditioning of LS system: {:.4e}.").format( + len(support), len(vander) - len(support), + deg, polyfitname(self.polybasis), + fitOut[1][2][0] / fitOut[1][2][-1]) + else: msg = None + self.coeffsLocal = fitOut[0][: len(support)] + if homogeneized: + self.coeffsGlobal = homogeneizedToFull( + tuple([deg + 1] * self.npar) + outDim, + self.npar, P) + else: + self.coeffsGlobal = P.reshape(tuple([d + 1 for d in deg]) + outDim) + return fitOut[1][1] == vander.shape[1], msg + diff --git a/rrompy/utilities/poly_fitting/radial_basis/val.py b/rrompy/utilities/poly_fitting/radial_basis/val.py index 51f4c5b..d0a334f 100644 --- a/rrompy/utilities/poly_fitting/radial_basis/val.py +++ b/rrompy/utilities/poly_fitting/radial_basis/val.py @@ -1,54 +1,52 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import deepcopy as copy import numpy as np -from rrompy.utilities.base.types import Np1D, Np2D, List, paramList, radialFun +from rrompy.utilities.base.types import Np1D, Np2D, paramList from rrompy.parameter import checkParameterList from rrompy.utilities.exception_manager import RROMPyException from rrompy.utilities.poly_fitting.polynomial import polyval as pvP -from .der import polyder from .kernel import radialGaussian, thinPlateSpline, multiQuadric __all__ = ['polyval'] -def polyval(x:paramList, c:radialFun, basis:str, m : List[int] = None, - scl : Np1D = None) -> Np2D: - cFun = polyder(c, basis, m = m, scl = scl) - x, _ = checkParameterList(x) +def polyval(x:paramList, cG:Np2D, cL:Np2D, supportPoints:paramList, + directionalWeights:Np1D, basis:str) -> Np2D: + x = checkParameterList(x)[0] basisp, basisr = basis.split("_") - c = pvP(x, cFun.globalCoeffs, basisp, m, scl) + c = pvP(x, cG, basisp) try: radialvalbase = {"GAUSSIAN" : radialGaussian, "THINPLATE" : thinPlateSpline, "MULTIQUADRIC" : multiQuadric }[basisr.upper()] except: raise RROMPyException("Radial basis not recognized.") csh = copy(c.shape) if len(csh) == 1: c = c.reshape(1, -1) for j in range(len(x)): - muDiff = (cFun.supportPoints.data - x[j]) * cFun.directionalWeights + muDiff = (supportPoints.data - x[j]) * directionalWeights r2j = np.sum(np.abs(muDiff) ** 2., axis = 1).reshape(1, -1) - val = radialvalbase(r2j).dot(cFun.localCoeffs) + val = radialvalbase(r2j).dot(cL) try: c[..., j] += val except: c[..., j] += val.flatten() if len(csh) == 1: c = c.flatten() return c diff --git a/rrompy/utilities/poly_fitting/radial_basis/vander.py b/rrompy/utilities/poly_fitting/radial_basis/vander.py index 5e9d6d0..086d74f 100644 --- a/rrompy/utilities/poly_fitting/radial_basis/vander.py +++ b/rrompy/utilities/poly_fitting/radial_basis/vander.py @@ -1,79 +1,79 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.utilities.poly_fitting.polynomial.vander import polyvander as pvP from rrompy.utilities.base.types import Np1D, Np2D, List, paramList from rrompy.parameter import checkParameterList from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert from .kernel import radialGaussian, thinPlateSpline, multiQuadric __all__ = ['rbvander', 'polyvander'] def rbvander(x:paramList, basis:str, reorder : List[int] = None, directionalWeights : Np1D = None) -> Np2D: """Compute radial-basis-Vandermonde matrix.""" - x, _ = checkParameterList(x) + x = checkParameterList(x)[0] x_un, idx_un = x.unique(return_inverse = True) nx = len(x) if len(x_un) < nx: raise RROMPyException("Sample points must be distinct.") del x_un x = x.data if directionalWeights is None: directionalWeights = np.ones(x.shape[1]) RROMPyAssert(len(directionalWeights), x.shape[1], "Number of directional weights") try: radialkernel = {"GAUSSIAN" : radialGaussian, "THINPLATE" : thinPlateSpline, "MULTIQUADRIC" : multiQuadric }[basis.upper()] except: raise RROMPyException("Radial basis not recognized.") r2 = np.zeros((nx * (nx - 1) // 2 + 1), dtype = float) idxInv = np.zeros(nx ** 2, dtype = int) if reorder is not None: x = x[reorder] for j in range(nx): idx = j * (nx - 1) - j * (j + 1) // 2 II = np.arange(j + 1, nx) r2[idx + II] = np.sum(np.abs((x[II] - x[j]) * directionalWeights) ** 2., axis = 1) idxInv[j * nx + II] = idx + II idxInv[II * nx + j] = idx + II Van = radialkernel(r2)[idxInv].reshape((nx, nx)) return Van def polyvander(x:paramList, degs:List[int], basis:str, derIdxs : List[List[List[int]]] = None, reorder : List[int] = None, directionalWeights : Np1D = None, scl : Np1D = None) -> Np2D: """ Compute radial-basis-inclusive Hermite-Vandermonde matrix with specified derivative directions. """ if derIdxs is not None and np.sum(np.sum(derIdxs)) > 0: raise RROMPyException(("Cannot take derivatives of radial basis " "function.")) basisp, basisr = basis.split("_") VanR = rbvander(x, basisr, reorder = reorder, directionalWeights = directionalWeights) VanP = pvP(x, degs, basisp, derIdxs = derIdxs, reorder = reorder, scl = scl) return np.block([[VanR, VanP], [VanP.T.conj(), np.zeros(tuple([VanP.shape[1]] * 2))]]) diff --git a/setup.py b/setup.py index 32b1d45..64677fc 100644 --- a/setup.py +++ b/setup.py @@ -1,52 +1,52 @@ -# Copyright (C) 2015-2018 by the RBniCS authors +# Copyright (C) 2018 by the RROMPy authors # -# This file is part of RBniCS. +# This file is part of RROMPy. # -# RBniCS is free software: you can redistribute it and/or modify +# 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. # -# RBniCS is distributed in the hope that it will be useful, +# 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 RBniCS. If not, see . +# along with RROMPy. If not, see . # import os from setuptools import find_packages, setup rrompy_directory = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) #rrompy_directory = os.path.join(rrompy_directory, 'rrompy') setup(name="RROMPy", description="Rational reduced order modelling in Python", long_description="Rational reduced order modelling in Python", author="Davide Pradovera", author_email="davide.pradovera@epfl.ch", - version="1.6", + version="1.7", license="GNU Library or Lesser General Public License (LGPL)", classifiers=[ "Development Status :: 1 - Planning" "Intended Audience :: Developers", "Intended Audience :: Science/Research", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "Topic :: Scientific/Engineering :: Mathematics", "Topic :: Software Development :: Libraries :: Python Modules", ], packages=find_packages(rrompy_directory), setup_requires=[ "pytest-runner" ], tests_require=[ "pytest" ], zip_safe=False ) diff --git a/tests/hfengines/helmholtz_internal.py b/tests/hfengines/helmholtz_internal.py index 920dfcb..d6fde8c 100644 --- a/tests/hfengines/helmholtz_internal.py +++ b/tests/hfengines/helmholtz_internal.py @@ -1,99 +1,97 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -import pytest import os, shutil import numpy as np from rrompy.hfengines.linear_problem import ( HelmholtzSquareBubbleDomainProblemEngine, HelmholtzSquareBubbleProblemEngine, HelmholtzSquareTransmissionProblemEngine) def test_helmholtz_square_io(): solver = HelmholtzSquareBubbleProblemEngine(kappa = 4, theta = 1., n = 20, verbosity = 0) mu = 5 uh = solver.solve(mu)[0] assert np.isclose(solver.norm(uh), 145.0115, rtol = 1e-3) assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True), 1.19934e-11, rtol = 1e-1) assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True), solver.norm(solver.residual(uh, mu, duality = False)[0], dual = True, duality = False), rtol = 1e-1) if not os.path.isdir("./.pytest_cache"): os.mkdir("./.pytest_cache") filesOut = [x for x in os.listdir("./.pytest_cache") if (x[-4:] == ".pvd" and x[:9] == "outSquare")] filesOutData = [x for x in os.listdir("./.pytest_cache") if (x[-4:] == ".vtu" and x[:9] == "outSquare")] for fileOut in filesOut: os.remove("./.pytest_cache/" + fileOut) for fileOut in filesOutData: os.remove("./.pytest_cache/" + fileOut) solver.outParaview(uh, what = ["MESH", "ABS"], filename = ".pytest_cache/outSquare", forceNewFile = False) filesOut = [x for x in os.listdir("./.pytest_cache") if (x[-4:] == ".pvd" and x[:9] == "outSquare")] filesOutData = [x for x in os.listdir("./.pytest_cache") if (x[-4:] == ".vtu" and x[:9] == "outSquare")] assert len(filesOut) == 1 assert len(filesOutData) == 1 os.remove("./.pytest_cache/" + filesOut[0]) os.remove("./.pytest_cache/" + filesOutData[0]) def test_helmholtz_transmission_io(): solver = HelmholtzSquareTransmissionProblemEngine(nT = 1, nB = 2, theta = np.pi * 40 / 180, kappa = 4., n = 20, verbosity = 0) mu = 5. uh = solver.solve(mu)[0] assert np.isclose(solver.norm(uh), 138.6609, rtol = 1e-2) assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True), 3.7288565e-12, rtol = 1e-1) if not os.path.isdir("./.pytest_cache"): os.mkdir("./.pytest_cache") solver.outParaviewTimeDomain(uh, omega = mu, filename = ".pytest_cache/outTrans", forceNewFile = False, folder = True) filesOut = [x for x in os.listdir("./.pytest_cache/outTrans") if (x[-4:] == ".pvd" and x[:8] == "outTrans")] filesOutData = [x for x in os.listdir("./.pytest_cache/outTrans") if (x[-4:] == ".vtu" and x[:8] == "outTrans")] assert len(filesOut) == 1 assert len(filesOutData) == 20 shutil.rmtree("./.pytest_cache/outTrans") -@pytest.mark.xfail(reason = "no graphical interface") def test_helmholtz_domain_io(): solver = HelmholtzSquareBubbleDomainProblemEngine(kappa = 4, theta = 1., n = 10, mu0 = 1.5, verbosity = 0) mu = 1.5 uh = solver.solve(mu)[0] if not os.path.isdir("./.pytest_cache"): os.mkdir("./.pytest_cache") solver.plot(uh, save = "./.pytest_cache/outDomain", show = False) filesOut = [x for x in os.listdir("./.pytest_cache") if (x[-4:] == ".eps" and x[:9] == "outDomain")] assert len(filesOut) == 1 os.remove("./.pytest_cache/" + filesOut[0]) assert np.isclose(solver.norm(uh), 10.07843, rtol = 1e-2) assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True), 6.14454989e-13, rtol = 1e-1) diff --git a/tests/hfengines/laplace.py b/tests/hfengines/laplace.py index a76dbc1..409ea74 100644 --- a/tests/hfengines/laplace.py +++ b/tests/hfengines/laplace.py @@ -1,42 +1,42 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.hfengines.linear_problem import LaplaceDiskGaussian from rrompy.hfengines.linear_problem.bidimensional import LaplaceDiskGaussian2 def test_laplace_disk(): solver = LaplaceDiskGaussian(n = 20, verbosity = 0) mu = 1.5 solver.setSolver("BICG", {"tol" : 1e-15}) uh = solver.solve(mu)[0] assert np.isclose(solver.norm(uh), 1.053403077447029, rtol = 1e-2) assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True), - 3.4591353e-08, rtol = 1e-1) + 4.66728e-10, atol = 1e-7) assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True), solver.norm(solver.residual(uh, mu, duality = False)[0], dual = True, duality = False), rtol = 1e-1) def test_laplace_disk_2(): solver = LaplaceDiskGaussian2(n = 20, verbosity = 0) mu = [[0., 1.5]] mu = [0., 1.5] uh = solver.solve(mu)[0] assert np.isclose(solver.norm(uh), 1.0534030774205372, rtol = 1e-2) assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True), - 2.40043363e-08, rtol = 1e-1) + 2.40043363e-08, atol = 1e-7) diff --git a/tests/reduction_methods_1D/rational_interpolant_1d.py b/tests/reduction_methods_1D/rational_interpolant_1d.py index 5011114..b8bd6ac 100644 --- a/tests/reduction_methods_1D/rational_interpolant_1d.py +++ b/tests/reduction_methods_1D/rational_interpolant_1d.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.distributed import RationalInterpolant as RI +from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, ManualSampler as MS) from rrompy.parameter import checkParameterList 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", "sampler": QS([1.5, 6.5], "UNIFORM")} approx = RI(solver, 4., params, verbosity = 0) approx.setupApprox() out, err = capsys.readouterr() assert "poorly conditioned. Reducing E " in out assert len(err) == 0 assert np.isclose(approx.normErr(mu)[0], 2.9051e-4, atol = 1e-4) def test_well_cond(): mu = 1.5 solver = matrixFFT() 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() poles = approx.getPoles() for lambda_ in np.arange(1, 8): assert np.isclose(np.min(np.abs(poles - lambda_)), 0., atol = 1e-4) for mu in approx.mus: assert np.isclose(approx.normErr(mu)[0], 0., atol = 1e-8) def test_hermite(): mu = 1.5 solver = matrixFFT() sampler0 = QS([1., 7.], "CHEBYSHEV") points, _ = checkParameterList(np.tile(sampler0.generatePoints(4)(0), 3)) 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() poles = approx.getPoles() for lambda_ in np.arange(1, 8): assert np.isclose(np.min(np.abs(poles - lambda_)), 0., atol = 1e-4) for mu in approx.mus: assert np.isclose(approx.normErr(mu)[0], 0., atol = 1e-8) diff --git a/tests/reduction_methods_1D/rational_interpolant_greedy_1d.py b/tests/reduction_methods_1D/rational_interpolant_greedy_1d.py index 4ceb0ee..d57d9e2 100644 --- a/tests/reduction_methods_1D/rational_interpolant_greedy_1d.py +++ b/tests/reduction_methods_1D/rational_interpolant_greedy_1d.py @@ -1,94 +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 matrix_fft import matrixFFT -from rrompy.reduction_methods.distributed_greedy import \ - RationalInterpolantGreedy as RIG +from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RIG from rrompy.parameter.parameter_sampling import QuadratureSampler as QS def test_lax_tolerance(capsys): mu = 2.25 solver = matrixFFT() params = {"POD": True, "sampler": QS([1.5, 6.5], "UNIFORM"), "S": 4, "polybasis": "CHEBYSHEV", "greedyTol": 1e-2, "errorEstimatorKind": "bare", "trainSetGenerator": QS([1.5, 6.5], "CHEBYSHEV")} approx = RIG(solver, 4., params, verbosity = 100) 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(mu)[0], 2.169678e-4, rtol = 1e-1) def test_samples_at_poles(): solver = matrixFFT() params = {"POD": True, "sampler": QS([1.5, 6.5], "UNIFORM"), "S": 4, "nTestPoints": 100, "polybasis": "CHEBYSHEV", "greedyTol": 1e-5, "errorEstimatorKind": "exact", "trainSetGenerator": QS([1.5, 6.5], "CHEBYSHEV")} approx = RIG(solver, 4., params, verbosity = 0) approx.greedy() for mu in approx.mus: assert np.isclose(approx.normErr(mu)[0] / (1e-15+approx.normHF(mu)[0]), 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(np.array(approx.mus(0)) - lambda_)), 0., atol = 1e-1) def test_maxIter(): solver = matrixFFT() params = {"POD": True, "sampler": QS([1.5, 6.5], "UNIFORM"), "S": 5, "nTestPoints": 500, "polybasis": "CHEBYSHEV", "greedyTol": 1e-6, "maxIter": 10, "errorEstimatorKind": "basic", "trainSetGenerator": QS([1.5, 6.5], "CHEBYSHEV")} 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 def test_load_copy(capsys): mu = 3. solver = matrixFFT() params = {"POD": True, "sampler": QS([1.5, 6.5], "UNIFORM"), "S": 4, "nTestPoints": 100, "polybasis": "CHEBYSHEV", "greedyTol": 1e-5, "errorEstimatorKind": "exact", "trainSetGenerator": QS([1.5, 6.5], "CHEBYSHEV")} approx1 = RIG(solver, 4., params, verbosity = 100) approx1.greedy() err1 = approx1.normErr(mu)[0] out, err = capsys.readouterr() assert "Solving HF model for mu =" in out assert len(err) == 0 approx2 = RIG(solver, 4., params, verbosity = 100) approx2.setTrainedModel(approx1) approx2.setHF(mu, approx1.uHF) err2 = approx2.normErr(mu)[0] out, err = capsys.readouterr() assert "Solving HF model for mu =" not in out assert len(err) == 0 assert np.isclose(err1, err2, rtol = 1e-10) diff --git a/tests/reduction_methods_1D/rational_pade_1d.py b/tests/reduction_methods_1D/rational_pade_1d.py deleted file mode 100644 index 99e9aaf..0000000 --- a/tests/reduction_methods_1D/rational_pade_1d.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -import os -import numpy as np -from matrix_fft import matrixFFT -from rrompy.reduction_methods.centered import RationalPade as RP - -def test_rho(): - mu = 1.5 - mu0 = 2. + 1.j - solver = matrixFFT() - uh = solver.solve(mu)[0] - params = {"POD": False, "M": 9, "N": 10, "S": 11, "robustTol": 1e-6} - approx = RP(solver, mu0, params, verbosity = 0) - approx.setupApprox() - - 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] == "outPad")] - for fileOut in filesOut: os.remove("./.pytest_cache/" + fileOut) - fileStored = approx.storeTrainedModel(".pytest_cache/outPad") - filesOut = [x for x in os.listdir("./.pytest_cache") if - (x[-4:] == ".pkl" and x[:6] == "outPad")] - assert len(filesOut) == 1 - assert filesOut[0] == fileStored[- len(filesOut[0]) :] - uhP1 = approx.getApprox(mu)[0] - errP = approx.getErr(mu)[0] - errNP = approx.normErr(mu)[0] - myerrP = uhP1 - uh - assert np.allclose(np.abs(errP - myerrP), 0., rtol = 1e-3) - assert np.isclose(solver.norm(errP), errNP, rtol = 1e-3) - resP = approx.getRes(mu)[0] - 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 = RP(solver, mu0, {"S": 3}, verbosity = 0) - approx.loadTrainedModel(fileStored) - for fileOut in filesOut: os.remove("./.pytest_cache/" + fileOut) - uhP2 = approx.getApprox(mu)[0] - 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)[0] - params = {"POD": True, "M": 14, "N": 15, "S": 10} - approx = RP(solver, mu0, params, verbosity = 0) - approx.setupApprox() - - out, err = capsys.readouterr() - assert "Prescribed E is too small. Decreasing M." in out - assert "Prescribed E is too small. Decreasing N." in out - assert len(err) == 0 - - uhP = approx.getApprox(mu)[0] - errP = approx.getErr(mu)[0] - errNP = approx.normErr(mu)[0] - assert np.allclose(np.abs(errP - (uhP - uh)), 0., rtol = 1e-3) - assert np.isclose(errNP, 3.5197568e-07, rtol = 1e-1) - - poles, ress = approx.getResidues() - condres = np.linalg.cond(solver.innerProduct(ress, ress)) - assert np.isclose(condres, 192.1791778, rtol = 1e-3) - - assert np.isclose(np.min(np.abs(poles - 2.)), 0., atol = 1e-3) - 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) diff --git a/tests/reduction_methods_1D/rb_centered_1d.py b/tests/reduction_methods_1D/rb_centered_1d.py deleted file mode 100644 index 717b5b2..0000000 --- a/tests/reduction_methods_1D/rb_centered_1d.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -import numpy as np -from matrix_fft import matrixFFT -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)[0] - params = {"POD": True, "R": 5, "S": 10} - approx = RBC(solver, mu0, params, verbosity = 0) - approx.setupApprox() - - uhP = approx.getApprox(mu)[0] - errP = approx.getErr(mu)[0] - _ = approx.normErr(mu)[0] - assert np.allclose(np.abs(errP - (uhP - uh)), 0., rtol = 1e-3) - - 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, "S": 10} - approx = RBC(solver, mu0, params, verbosity = 0) - approx.setupApprox() - assert np.isclose(approx.normErr(mu0)[0], 0., atol = 1e-10) - -def test_load_copy(capsys): - mu = 1.5 - mu0 = 2. + 1.j - solver = matrixFFT() - params = {"POD": True, "S": 10} - approx1 = RBC(solver, mu0, params, verbosity = 100) - approx1.setupApprox() - err1 = approx1.normErr(mu)[0] - out, err = capsys.readouterr() - assert "Solving HF model for mu =" in out - assert len(err) == 0 - approx2 = RBC(solver, mu0, params, verbosity = 100) - approx2.setTrainedModel(approx1.trainedModel) - approx2.setHF(mu, approx1.uHF) - err2 = approx2.normErr(mu)[0] - out, err = capsys.readouterr() - assert "Solving HF model for mu =" not in out - assert len(err) == 0 - assert np.isclose(err1, err2, rtol = 1e-10) - diff --git a/tests/reduction_methods_1D/rb_distributed_1d.py b/tests/reduction_methods_1D/reduced_basis_1d.py similarity index 89% rename from tests/reduction_methods_1D/rb_distributed_1d.py rename to tests/reduction_methods_1D/reduced_basis_1d.py index 3b1f288..3cd2a78 100644 --- a/tests/reduction_methods_1D/rb_distributed_1d.py +++ b/tests/reduction_methods_1D/reduced_basis_1d.py @@ -1,57 +1,57 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from matrix_fft import matrixFFT -from rrompy.reduction_methods.distributed import RBDistributed as RBD +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, ManualSampler as MS) from rrompy.parameter import checkParameterList def test_LS(): solver = matrixFFT() params = {"POD": True, "R": 5, "S": 10, "sampler": QS([1., 7.], "CHEBYSHEV")} - approx = RBD(solver, 4., params, verbosity = 0) + approx = RB(solver, 4., params, verbosity = 0) approx.setupApprox() for mu in approx.mus: assert not np.isclose(approx.normErr(mu)[0], 0., atol = 1e-7) approx.POD = False approx.setupApprox() for mu in approx.mus[approx.R :]: assert not np.isclose(approx.normErr(mu)[0], 0., atol = 1e-3) def test_interp(): solver = matrixFFT() params = {"POD": False, "S": 10, "sampler": QS([1., 7.], "CHEBYSHEV")} - approx = RBD(solver, 4., params, verbosity = 0) + approx = RB(solver, 4., params, verbosity = 0) approx.setupApprox() for mu in approx.mus: assert np.isclose(approx.normErr(mu)[0], 0., atol = 1e-7) def test_hermite(): mu = 1.5 solver = matrixFFT() sampler0 = QS([1., 7.], "CHEBYSHEV") points, _ = checkParameterList(np.tile(sampler0.generatePoints(4)(0), 3)) params = {"POD": True, "S": 12, "sampler": MS([1., 7.], points = points)} - approx = RBD(solver, 4., params, verbosity = 0) + approx = RB(solver, 4., params, verbosity = 0) approx.setupApprox() for mu in approx.mus: assert np.isclose(approx.normErr(mu)[0], 0., atol = 1e-8) diff --git a/tests/reduction_methods_1D/rb_distributed_greedy_1d.py b/tests/reduction_methods_1D/reduced_basis_greedy_1d.py similarity index 88% rename from tests/reduction_methods_1D/rb_distributed_greedy_1d.py rename to tests/reduction_methods_1D/reduced_basis_greedy_1d.py index 512a049..0086ab0 100644 --- a/tests/reduction_methods_1D/rb_distributed_greedy_1d.py +++ b/tests/reduction_methods_1D/reduced_basis_greedy_1d.py @@ -1,60 +1,59 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from matrix_fft import matrixFFT -from rrompy.reduction_methods.distributed_greedy import RBDistributedGreedy \ - as RBDG +from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RBG from rrompy.parameter.parameter_sampling import QuadratureSampler as QS def test_lax_tolerance(capsys): mu = 2.25 solver = matrixFFT() params = {"POD": True, "sampler": QS([1.5, 6.5], "UNIFORM"), "S": 4, "greedyTol": 1e-2, "trainSetGenerator": QS([1.5, 6.5], "CHEBYSHEV")} - approx = RBDG(solver, 4., params, verbosity = 10) + approx = RBG(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(mu)[0], 1.5056e-05, rtol = 1e-1) def test_samples_at_poles(): solver = matrixFFT() params = {"POD": True, "sampler": QS([1.5, 6.5], "UNIFORM"), "S": 4, "nTestPoints": 100, "greedyTol": 1e-5, "trainSetGenerator": QS([1.5, 6.5], "CHEBYSHEV")} - approx = RBDG(solver, 4., params, verbosity = 0) + approx = RBG(solver, 4., params, verbosity = 0) approx.greedy() for mu in approx.mus: assert np.isclose(approx.normErr(mu)[0] / (1e-15+approx.normHF(mu)[0]), 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(np.array(approx.mus(0)) - lambda_)), 0., atol = 1e-1) diff --git a/tests/reduction_methods_multiD/rational_interpolant_2d.py b/tests/reduction_methods_multiD/rational_interpolant_2d.py index fe75aa8..0788552 100644 --- a/tests/reduction_methods_multiD/rational_interpolant_2d.py +++ b/tests/reduction_methods_multiD/rational_interpolant_2d.py @@ -1,79 +1,79 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from matrix_random import matrixRandom -from rrompy.reduction_methods.distributed import RationalInterpolant as RI +from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, ManualSampler as MS) def test_monomials(capsys): mu = [5.05, 7.1] mu0 = [5., 7.] solver = matrixRandom() uh = solver.solve(mu)[0] params = {"POD": False, "M": 4, "N": 4, "S": [4, 4], "robustTol": 1e-6, "interpRcond": 1e-3, "polybasis": "MONOMIAL", "sampler": QS([[4.9, 6.85], [5.1, 7.15]], "UNIFORM")} approx = RI(solver, mu0, params, verbosity = 0) approx.setupApprox() uhP1 = approx.getApprox(mu)[0] errP = approx.getErr(mu)[0] errNP = approx.normErr(mu)[0] myerrP = uhP1 - uh assert np.allclose(np.abs(errP - myerrP), 0., rtol = 1e-3) assert np.isclose(solver.norm(errP), errNP, rtol = 1e-3) resP = approx.getRes(mu)[0] 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) assert np.isclose(errNP / solver.norm(uh), 5.2667e-05, rtol = 1e-1) out, err = capsys.readouterr() print(out) assert ("poorly conditioned. Reducing E" in out) assert len(err) == 0 def test_well_cond(): mu = [5.05, 7.1] mu0 = [5., 7.] solver = matrixRandom() params = {"POD": True, "M": 3, "N": 3, "S": [4, 4], "interpRcond": 1e-10, "polybasis": "CHEBYSHEV", "sampler": QS([[4.9, 6.85], [5.1, 7.15]], "UNIFORM")} approx = RI(solver, mu0, params, verbosity = 0) approx.setupApprox() print(approx.normErr(mu)[0] / approx.normHF(mu)[0]) assert np.isclose(approx.normErr(mu)[0] / approx.normHF(mu)[0], 5.98695e-05, rtol = 1e-1) def test_hermite(): mu = [5.05, 7.1] mu0 = [5., 7.] solver = matrixRandom() sampler0 = QS([[4.9, 6.85], [5.1, 7.15]], "UNIFORM") params = {"POD": True, "M": 3, "N": 3, "S": [25], "polybasis": "CHEBYSHEV", "sampler": MS([[4.9, 6.85], [5.1, 7.15]], points = sampler0.generatePoints([3, 3]))} approx = RI(solver, mu0, params, verbosity = 0) approx.setupApprox() assert np.isclose(approx.normErr(mu)[0] / approx.normHF(mu)[0], 0.000236598, rtol = 1e-1) diff --git a/tests/reduction_methods_multiD/rational_pade_2d.py b/tests/reduction_methods_multiD/rational_pade_2d.py deleted file mode 100644 index 01ea5b7..0000000 --- a/tests/reduction_methods_multiD/rational_pade_2d.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -import numpy as np -from matrix_random import matrixRandom -from rrompy.reduction_methods.centered import RationalPade as RP - -def test_rho(): - mu = [5.5, 7.5] - mu0 = [5., 7.] - solver = matrixRandom() - uh = solver.solve(mu)[0] - params = {"POD": False, "M": 3, "N": 3, "S": 10, "robustTol": 1e-6} - approx = RP(solver, mu0, params, verbosity = 0) - approx.setupApprox() - - uhP1 = approx.getApprox(mu)[0] - errP = approx.getErr(mu)[0] - errNP = approx.normErr(mu)[0] - myerrP = uhP1 - uh - assert np.allclose(np.abs(errP - myerrP), 0., rtol = 1e-3) - assert np.isclose(solver.norm(errP), errNP, rtol = 1e-3) - resP = approx.getRes(mu)[0] - 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) - -def test_E_warn(capsys): - mu = [5.5, 7.5] - mu0 = [5., 7.] - solver = matrixRandom() - uh = solver.solve(mu)[0] - params = {"POD": True, "M": 3, "N": 4, "S": 10} - approx = RP(solver, mu0, params, verbosity = 0) - approx.setupApprox() - - out, err = capsys.readouterr() - assert "Prescribed E is too small. Decreasing M" not in out - assert "Prescribed E is too small. Decreasing N" in out - assert len(err) == 0 - - uhP = approx.getApprox(mu)[0] - uhNP = approx.normApprox(mu)[0] - errP = approx.getErr(mu)[0] - errNP = approx.normErr(mu)[0] - assert np.allclose(np.abs(errP - (uhP - uh)), 0., rtol = 1e-3) - assert np.isclose(errNP / uhNP, 1.41336e-2, rtol = 1e-1) - diff --git a/tests/reduction_methods_multiD/rb_centered_2d.py b/tests/reduction_methods_multiD/rb_centered_2d.py deleted file mode 100644 index ef558a5..0000000 --- a/tests/reduction_methods_multiD/rb_centered_2d.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -import numpy as np -from matrix_random import matrixRandom -from rrompy.reduction_methods.centered import RBCentered as RBC - -def test_R(): - mu = [5.5, 7.5] - mu0 = [5., 7.] - solver = matrixRandom() - uh = solver.solve(mu)[0] - uhN = solver.norm(uh) - params = {"POD": True, "S": 10} - approx = RBC(solver, mu0, params, verbosity = 0) - approx.setupApprox() - - uhP = approx.getApprox(mu)[0] - uhNP = approx.normApprox(mu)[0] - errP = approx.getErr(mu)[0] - errNP = approx.normErr(mu)[0] - - assert np.allclose(np.abs(errP - (uhP - uh)), 0., rtol = 1e-3) - assert np.isclose(uhN, uhNP, rtol = 1e-1) - assert np.isclose(errNP / uhNP, 0., atol = 1e-1) - -def test_moments(): - mu0 = [2 + .5j, 3] - solver = matrixRandom() - params = {"POD": True, "S": 10} - approx = RBC(solver, mu0, params, verbosity = 0) - approx.setupApprox() - assert np.isclose(approx.normErr(mu0)[0], 0., atol = 1e-10) - -def test_load_copy(capsys): - mu = [1.5, 3. + 1j] - mu0 = [2 + .5j, 3] - solver = matrixRandom() - params = {"POD": True, "S": 10} - approx1 = RBC(solver, mu0, params, verbosity = 100) - approx1.setupApprox() - err1 = approx1.normErr(mu)[0] - out, err = capsys.readouterr() - assert "Solving HF model for mu =" in out - assert len(err) == 0 - approx2 = RBC(solver, mu0, params, verbosity = 100) - approx2.setTrainedModel(approx1.trainedModel) - approx2.setHF(mu, approx1.uHF) - err2 = approx2.normErr(mu)[0] - out, err = capsys.readouterr() - assert "Solving HF model for mu =" not in out - assert len(err) == 0 - assert np.isclose(err1, err2, rtol = 1e-10) - diff --git a/tests/reduction_methods_multiD/rb_distributed_2d.py b/tests/reduction_methods_multiD/reduced_basis_2d.py similarity index 90% rename from tests/reduction_methods_multiD/rb_distributed_2d.py rename to tests/reduction_methods_multiD/reduced_basis_2d.py index 8182e2d..5a66aa1 100644 --- a/tests/reduction_methods_multiD/rb_distributed_2d.py +++ b/tests/reduction_methods_multiD/reduced_basis_2d.py @@ -1,62 +1,62 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from matrix_random import matrixRandom -from rrompy.reduction_methods.distributed import RBDistributed as RBD +from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, ManualSampler as MS) from rrompy.parameter import checkParameterList def test_LS(): mu0 = [2, 3] solver = matrixRandom() params = {"POD": True, "R": 5, "S": [3, 3], "sampler": QS([[0., 4.], [1., 5.]], "CHEBYSHEV")} - approx = RBD(solver, mu0, params, verbosity = 0) + approx = RB(solver, mu0, params, verbosity = 0) approx.setupApprox() for mu in approx.mus: assert not np.isclose(approx.normErr(mu)[0], 0., atol = 1e-7) approx.POD = False approx.setupApprox() for mu in approx.mus[approx.R :]: assert not np.isclose(approx.normErr(mu)[0], 0., atol = 1e-3) def test_interp(): mu0 = [2, 3] solver = matrixRandom() params = {"POD": False, "S": [3, 3], "sampler": QS([[0., 4.], [1., 5.]], "CHEBYSHEV")} - approx = RBD(solver, mu0, params, verbosity = 0) + approx = RB(solver, mu0, params, verbosity = 0) approx.setupApprox() for mu in approx.mus: assert np.isclose(approx.normErr(mu)[0], 0., atol = 1e-7) def test_hermite(): mu0 = [2, 3] solver = matrixRandom() sampler0 = QS([[0., 4.], [1., 5.]], "CHEBYSHEV") points, _ = checkParameterList(np.tile( sampler0.generatePoints([2, 2]).data, [3, 1])) params = {"POD": True, "S": [12], "sampler": MS([[0., 4.], [1., 5.]], points = points)} - approx = RBD(solver, mu0, params, verbosity = 0) + approx = RB(solver, mu0, params, verbosity = 0) approx.setupApprox() for mu in approx.mus: assert np.isclose(approx.normErr(mu)[0], 0., atol = 1e-8) diff --git a/tests/utilities/basic_routines.py b/tests/utilities/basic_routines.py index dfb251e..55d620b 100644 --- a/tests/utilities/basic_routines.py +++ b/tests/utilities/basic_routines.py @@ -1,66 +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 pytest import numpy as np from rrompy.utilities.exception_manager import RROMPyException, RROMPyWarning from rrompy.utilities.base import (findDictStrKey, purgeDict, purgeList, - lowDiscrepancy, verbosityDepth) + verbosityDepth) +from rrompy.utilities.numerical import lowDiscrepancy def test_exception(): with pytest.raises(Exception): raise RROMPyException("This is an exception!") def test_warning(capsys): RROMPyWarning("This is a warning.") out, err = capsys.readouterr() assert "This is a warning." in out assert len(err) == 0 def test_dict_list(): dictBase = {str(x): str(x**2) for x in range(10)} assert findDictStrKey("-1", dictBase.keys()) is None assert findDictStrKey("5", dictBase.keys()) == "5" dictPurged = purgeDict(dictBase, [str(x) for x in range(4)], silent = True) assert len(dictPurged.keys()) == 4 listBase = list(dictBase.values()) listPurged = purgeList(listBase, [str(x**2) for x in range(4,6)], silent = True) assert len(listPurged) == 2 def test_vandercorput(): idxs = lowDiscrepancy(8) assert np.all(idxs == np.array([0,4,2,6,1,5,3,7])) def test_verbositydepth(capsys): verbosityDepth("INIT", "Start message", timestamp = False) out, err = capsys.readouterr() assert out == "┌Start message\n" assert len(err) == 0 verbosityDepth("MAIN", "Main message", end = "\n\n", timestamp = False) out, err = capsys.readouterr() assert out == "├Main message\n\n" assert len(err) == 0 verbosityDepth("INIT", "Start message2") verbosityDepth("DEL", "Delete message2") verbosityDepth("DEL", "Delete message") with pytest.raises(Exception): verbosityDepth("DEL", "Wrong deletion") diff --git a/tests/utilities/heaviside_fitting.py b/tests/utilities/heaviside_fitting.py new file mode 100644 index 0000000..5f625c4 --- /dev/null +++ b/tests/utilities/heaviside_fitting.py @@ -0,0 +1,72 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from rrompy.utilities.poly_fitting.heaviside import (polybases, polyfitname, + polydomcoeff, + heaviside2rational, + rational2heaviside) +from rrompy.utilities.poly_fitting.polynomial.polynomial_interpolator import \ + PolynomialInterpolator + +def test_monomial_heaviside(): + polyhsname = "MONOMIAL_HEAVISIDE" + assert polyhsname in polybases + fitname = polyfitname(polyhsname) + domcoeff = polydomcoeff(5, polyhsname) + assert fitname == "polyfit_heaviside" + assert np.isclose(domcoeff, 1., rtol = 1e-5) + + pls = np.array([-5., 0., 2.]) + cHS = np.array([4., 1., -10., -25., 4., .25]) + cNum = np.array([-10., 195., -120., -15.5, 4.75, .25]) + cDen = np.array([0., -10., 3., 1.]) + cNum /= np.linalg.norm(cDen) + cDen /= np.linalg.norm(cDen) + + numI = PolynomialInterpolator() + numI.coeffs, numI.npar, numI.polybasis = cNum, 1, "MONOMIAL" + denI = PolynomialInterpolator() + denI.coeffs, denI.npar, denI.polybasis = cDen, 1, "MONOMIAL" + + numA, denA = heaviside2rational(cHS, pls, [-10., 10.]) + cA, plsA, _ = rational2heaviside(numI, denI, [-10., 10.]) + numA.coeffs /= (denA.coeffs[1] / cDen[1]) + denA.coeffs /= (denA.coeffs[1] / cDen[1]) + + assert np.allclose(numA.coeffs, numI.coeffs, atol = 1e-5) + assert np.allclose(denA.coeffs, denI.coeffs, atol = 1e-5) + assert np.allclose(cA, cHS, atol = 1e-5) + assert np.allclose(plsA, pls, atol = 1e-5) + +def test_chebyshev_heaviside(): + polyhsname = "CHEBYSHEV_HEAVISIDE" + assert polyhsname in polybases + fitname = polyfitname(polyhsname) + domcoeff = polydomcoeff(5, polyhsname) + assert fitname == "chebfit_heaviside" + assert np.isclose(domcoeff, 16, rtol = 1e-5) + + pls = np.array([-5., 0., 2. + 1.j, 7]) + cHS = np.array([4., 1., 25., -10., -25., 4., .25], dtype = np.complex) + numA, denA = heaviside2rational(cHS, pls, [-10., 10.]) + cA, plsA, _ = rational2heaviside(numA, denA, [-10., 10.]) + + assert np.allclose(cA, cHS, atol = 1e-5) + assert np.allclose(plsA, pls, atol = 1e-5) + diff --git a/tests/utilities/parameter_sampling.py b/tests/utilities/parameter_sampling.py index b94792e..b1ea088 100644 --- a/tests/utilities/parameter_sampling.py +++ b/tests/utilities/parameter_sampling.py @@ -1,60 +1,59 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.parameter.parameter_sampling import (ManualSampler, QuadratureSampler, RandomSampler, FFTSampler) from rrompy.parameter import checkParameter def test_manual(): sampler = ManualSampler(lims = [0., 3.], points = np.linspace(0, 3, 101), - scaling = lambda x: np.power(x, 2.), - scalingInv = lambda x: np.power(x, .5)) + scalingExp = 2.) assert sampler.name() == "ManualSampler" x = sampler.generatePoints(10) assert np.allclose(x(0), np.linspace(0, 3, 101)[:10], rtol = 1e-5) def test_quadrature(): sampler = QuadratureSampler(lims = [0., 3.], kind = "CHEBYSHEV") x = sampler.generatePoints(9) assert np.isclose(x(0)[2], 1.5, rtol = 1e-5) def test_random(): sampler = RandomSampler(lims = [0., 3.], kind = "SOBOL", seed = 13432) x = sampler.generatePoints(100) assert np.isclose(x(0)[47], 0.55609130859375, rtol = 1e-5) def test_fft(): sampler = FFTSampler(lims = [-1., 1.]) x = sampler.generatePoints(100) assert np.allclose(np.power(x(0), 100), 1., rtol = 1e-5) def test_2D(): sampler = QuadratureSampler(lims = [(0., 0.), (3., 1.)], kind = "GAUSSLEGENDRE") x = sampler.generatePoints((9, 5)) assert sum(np.isclose(x(0), 1.5)) == 5 assert sum(np.isclose(x(1), .5)) == 9 def test_4D(): sampler = RandomSampler(lims = [tuple([0.] * 4), tuple([1.] * 4)], kind = "UNIFORM", seed = 1234) x = sampler.generatePoints(10) assert x.shape[1] == 4 assert checkParameter([x[0]]) == checkParameter([(0.191519450378892, 0.622108771039832, 0.437727739007115, 0.785358583713769)]) diff --git a/tests/utilities/radial_fitting.py b/tests/utilities/radial_fitting.py index 766e0a9..d10448b 100644 --- a/tests/utilities/radial_fitting.py +++ b/tests/utilities/radial_fitting.py @@ -1,161 +1,167 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.utilities.poly_fitting import customFit from rrompy.utilities.poly_fitting.radial_basis import (radialGaussian, thinPlateSpline, multiQuadric, polybases, polyfitname, - polydomcoeff, - radialFunction, + polydomcoeff, polyval, polyvander, homogeneizedpolyvander) from rrompy.utilities.poly_fitting.polynomial import homogeneizedToFull from rrompy.parameter import checkParameterList def test_monomial_gaussian(): polyrbname = "MONOMIAL_GAUSSIAN" assert polyrbname in polybases fitname = polyfitname(polyrbname) domcoeff = polydomcoeff(5, polyrbname) assert fitname == "polyfit_gaussian" assert np.isclose(domcoeff, 1., rtol = 1e-5) directionalWeights = np.array([5.]) xSupp = checkParameterList(np.arange(-1, 3), 1)[0] cRBCoeffs = np.array([-1., 3., -3., 1., 1., 2., -.5]) - cRB = radialFunction(directionalWeights = directionalWeights, - supportPoints = xSupp, localCoeffs = cRBCoeffs[: 4], - globalCoeffs = cRBCoeffs[4 :]) + + globalCoeffs = cRBCoeffs[4 :] + localCoeffs = cRBCoeffs[: 4] + ySupp = 1 + 2. * xSupp.data - .5 * xSupp.data ** 2. xx = np.linspace(-2., 3., 100) - yy = polyval(checkParameterList(xx, 1)[0], cRB, polyrbname) + yy = polyval(checkParameterList(xx, 1)[0], globalCoeffs, localCoeffs, + xSupp, directionalWeights, polyrbname) yyman = 1. + 2. * xx - .5 * xx ** 2. for j, xc in enumerate(np.arange(-1, 3)): r2j = (5. * (xx - xc)) ** 2. rbj = radialGaussian(r2j) assert np.allclose(rbj, np.exp(-.5 * r2j)) - yyman += cRB.localCoeffs[j] * rbj - ySupp += cRB.localCoeffs[j] * radialGaussian((directionalWeights[0] - * (xSupp.data - xc)) ** 2.) + yyman += localCoeffs[j] * rbj + ySupp += localCoeffs[j] * radialGaussian((directionalWeights[0] + * (xSupp.data - xc)) ** 2.) assert np.allclose(yy, yyman, atol = 1e-5) VanT = polyvander(xSupp, [2], polyrbname, directionalWeights = directionalWeights) ySupp = np.pad(ySupp.flatten(), (0, len(VanT) - len(xSupp)), "constant") out = customFit(VanT, ySupp) assert np.allclose(out, cRBCoeffs, atol = 1e-8) def test_legendre_thinplate(): polyrbname = "LEGENDRE_THINPLATE" assert polyrbname in polybases fitname = polyfitname(polyrbname) domcoeff = polydomcoeff(5, polyrbname) assert fitname == "legfit_thinplate" assert np.isclose(domcoeff, 63. / 8, rtol = 1e-5) directionalWeights = np.array([.5]) xSupp = checkParameterList(np.arange(-1, 3), 1)[0] cRBCoeffs = np.array([-1., 3., -3., 1., 1., 2., -.5]) - cRB = radialFunction(directionalWeights = directionalWeights, - supportPoints = xSupp, localCoeffs = cRBCoeffs[: 4], - globalCoeffs = cRBCoeffs[4 :]) + + localCoeffs = cRBCoeffs[: 4] + globalCoeffs = cRBCoeffs[4 :] + ySupp = 1 + 2. * xSupp.data - .5 * (.5 * (3. * xSupp.data ** 2. - 1.)) xx = np.linspace(-2., 3., 100) - yy = polyval(checkParameterList(xx, 1)[0], cRB, polyrbname) + yy = polyval(checkParameterList(xx, 1)[0], globalCoeffs, localCoeffs, + xSupp, directionalWeights, polyrbname) yyman = 1. + 2. * xx - .5 * (.5 * (3. * xx ** 2. - 1.)) for j, xc in enumerate(np.arange(-1, 3)): r2j = (directionalWeights[0] * (xx - xc)) ** 2. rbj = thinPlateSpline(r2j) assert np.allclose(rbj, .5 * r2j * np.log(np.finfo(float).eps + r2j)) - yyman += cRB.localCoeffs[j] * rbj - ySupp += cRB.localCoeffs[j] * thinPlateSpline((directionalWeights[0] + yyman += localCoeffs[j] * rbj + ySupp += localCoeffs[j] * thinPlateSpline((directionalWeights[0] * (xSupp.data - xc)) ** 2.) assert np.allclose(yy, yyman, atol = 1e-5) VanT = polyvander(xSupp, [2], polyrbname, directionalWeights = directionalWeights) ySupp = np.pad(ySupp.flatten(), (0, len(VanT) - len(xSupp)), "constant") out = customFit(VanT, ySupp) assert np.allclose(out, cRBCoeffs, atol = 1e-8) def test_chebyshev_multiquadric(): polyrbname = "CHEBYSHEV_MULTIQUADRIC" assert polyrbname in polybases fitname = polyfitname(polyrbname) domcoeff = polydomcoeff(5, polyrbname) assert fitname == "chebfit_multiquadric" assert np.isclose(domcoeff, 16, rtol = 1e-5) directionalWeights = np.array([1.]) xSupp = checkParameterList(np.arange(-1, 3), 1)[0] cRBCoeffs = np.array([-1., 3., -3., 1., 1., 2., -.5]) - cRB = radialFunction(directionalWeights = directionalWeights, - supportPoints = xSupp, localCoeffs = cRBCoeffs[: 4], - globalCoeffs = cRBCoeffs[4 :]) + + localCoeffs = cRBCoeffs[: 4] + globalCoeffs = cRBCoeffs[4 :] + ySupp = 1 + 2. * xSupp.data - .5 * (2. * xSupp.data ** 2. - 1.) xx = np.linspace(-2., 3., 100) - yy = polyval(checkParameterList(xx, 1)[0], cRB, polyrbname) + yy = polyval(checkParameterList(xx, 1)[0], globalCoeffs, localCoeffs, + xSupp, directionalWeights, polyrbname) yyman = 1. + 2. * xx - .5 * (2. * xx ** 2. - 1.) for j, xc in enumerate(np.arange(-1, 3)): r2j = (directionalWeights[0] * (xx - xc)) ** 2. rbj = multiQuadric(r2j) assert np.allclose(rbj, np.power(r2j + 1, -.5)) - yyman += cRB.localCoeffs[j] * rbj - ySupp += cRB.localCoeffs[j] * multiQuadric((directionalWeights[0] - * (xSupp.data - xc)) ** 2.) + yyman += localCoeffs[j] * rbj + ySupp += localCoeffs[j] * multiQuadric((directionalWeights[0] + * (xSupp.data - xc)) ** 2.) assert np.allclose(yy, yyman, atol = 1e-5) VanT = polyvander(xSupp, [2], polyrbname, directionalWeights = directionalWeights) ySupp = np.pad(ySupp.flatten(), (0, len(VanT) - len(xSupp)), "constant") out = customFit(VanT, ySupp) assert np.allclose(out, cRBCoeffs, atol = 1e-8) def test_total_degree_2d(): values = lambda x, y: (x - 3.) ** 2. * y - (x + 1.) * y ** 2. polyrbname = "CHEBYSHEV_GAUSSIAN" xs, ys = np.meshgrid(np.linspace(0., 4., 5), np.linspace(0., 4., 4)) xySupp = np.concatenate((xs.reshape(-1, 1), ys.reshape(-1, 1)), axis = 1) zs = values(xs, ys) zSupp = zs.flatten() deg = 3 directionalWeights = [2., 1.] VanT, _, reidxs = homogeneizedpolyvander(xySupp, deg, polyrbname, directionalWeights = directionalWeights) VanT = VanT[reidxs] VanT = VanT[:, reidxs] cFit = np.linalg.solve(VanT, np.pad(zSupp, (0, len(VanT) - len(zSupp)), 'constant')) globCoeff = homogeneizedToFull([deg + 1] * 2, 2, cFit[len(zSupp) :]) - cRB = radialFunction(directionalWeights = directionalWeights, - supportPoints = xySupp, - localCoeffs = cFit[: len(zSupp)], - globalCoeffs = globCoeff) + + localCoeffs = cFit[: len(zSupp)] + globalCoeffs = globCoeff + xx, yy = np.meshgrid(np.linspace(0., 4., 100), np.linspace(0., 4., 100)) xxyy = np.concatenate((xx.reshape(-1, 1), yy.reshape(-1, 1)), axis = 1) - zz = polyval(xxyy, cRB, polyrbname).reshape(xx.shape) + zz = polyval(xxyy, globalCoeffs, localCoeffs, xySupp, directionalWeights, + polyrbname).reshape(xx.shape) zzex = values(xx, yy) error = np.abs(zz - zzex) print(np.max(error)) assert np.max(error) < 1e-10 diff --git a/tests/utilities/sampling.py b/tests/utilities/sampling.py index f1b2975..a441267 100644 --- a/tests/utilities/sampling.py +++ b/tests/utilities/sampling.py @@ -1,78 +1,78 @@ # 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 (SamplingEngineLinear, - SamplingEngineLinearPOD) +from rrompy.sampling.standard import (SamplingEngineStandard, + SamplingEngineStandardPOD) from rrompy.parameter import parameterList def test_krylov(): N = 100 mu = 10. + .5j solver = MEB(verbosity = 0) solver.npar = 1 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 = SamplingEngineLinear(solver, verbosity = 0) + samplingEngine = SamplingEngineStandard(solver, verbosity = 0) samples = samplingEngine.iterSample([mu] * 5).data assert samples.shape == (100, 5) assert np.isclose(np.linalg.norm(samples), 37.02294804524299, rtol = 1e-5) def test_distributed(): N = 100 mus = parameterList(np.linspace(5, 15, 11) + .5j) solver = MEB(verbosity = 0) solver.npar = 1 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 = SamplingEngineLinear(solver, verbosity = 0) + samplingEngine = SamplingEngineStandard(solver, verbosity = 0) samples = samplingEngine.iterSample(mus).data assert samples.shape == (100, 11) assert np.isclose(np.linalg.norm(samples), 8.59778606421386, rtol = 1e-5) def test_distributed_pod(): N = 100 mus = np.linspace(5, 15, 11) + .5j solver = MEB(verbosity = 0) solver.npar = 1 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 = SamplingEngineLinearPOD(solver, verbosity = 0) + samplingEngine = SamplingEngineStandardPOD(solver, verbosity = 0) samples = samplingEngine.iterSample(mus).data assert samples.shape == (100, 11) assert np.isclose(np.linalg.norm(samples), 3.3166247903553994, rtol = 1e-5) assert np.isclose(np.linalg.cond(samples.conj().T.dot(samples)), 1., rtol = 1e-5)