Source code for twoscale.linear

# Copyright (C) 2026 - Alexis Salzman, Ecole Centrale de Nantes
#
# SPDX-License-Identifier:    LGPL-3.0-or-later
"""module that provides TwoScale linear solvers tools."""

import importlib

_ts_dolfinx_exist = False
try:
    importlib.import_module("twoscale.ts_cpp.dolfinx")
    _ts_dolfinx_exist = True
except ImportError:
    print("Twoscale dolfinx implementation not available")
    print("For now no other implementation exist")

if _ts_dolfinx_exist:
    print("Use Twoscale dolfinx implementation")
    from twoscale import core
    from dolfinx import mesh
    from dolfinx.fem import Function
    from dolfinx.fem import functionspace
    from dolfinx.fem import FunctionSpace
    from dolfinx.fem import create_interpolation_data
    from dolfinx.fem.bcs import DirichletBC
    from dolfinx_mpc.multipointconstraint import MultiPointConstraint
    from petsc4py import PETSc
    from petsc4py import typing as petsc_typing
    import typing

    from twoscale import util
    import pyvista as pv
    import numpy as np

    # =================================================================
[docs] def residual(A: PETSc.Mat, b: PETSc.Vec, x: PETSc.Vec, nb: petsc_typing.Scalar): """ Function to compute the relative residual of the linear problem A.x=b provided as argument. :param A: The matrix of the system :type A: Petsc Mat :param b: The right hand side vector of the system :type b: Petsc Vec :param x: The solution vector of the system (i.e. :math:`x=A^{-1}.b`) :type x: Petsc Vec :param nb: The value to resize the norm and make it relative. In general it is expected to be the norm of b. :type nb: PetscScalar :return: The relative norm of the residual: :math:`\\frac{|A.x-b|}{nb}` :rtype: PetscScalar """ R = b.duplicate() A.mult(x, R) # vi=PETSc.Viewer() # print("resi R0 vector") # vi(R) # print("resi b vector") # vi(b) # print("resi x vector") # vi(x) R.aypx(-1.0, b) # print("resi R vector") # vi(R) return R.norm() / nb
# =================================================================
[docs] def linearBasicLoop( sj: core.scaleJump, fine_space: FunctionSpace, Aff: PETSc.Mat, ADff: PETSc.Mat, Bf: PETSc.Vec, BDf: PETSc.Vec, efunc: core.enrichedFunction, enriched_field: Function, mpc: typing.Optional[MultiPointConstraint] = None, bc: list[DirichletBC] = [], itmx=10, eps=1.0e-3, ): """ Function to solve a linear problem by looping in between scale with the twoscale approach :param sj: The scale jump describing the two scale :param fine_space: The space describing fine scale discretization related to fine scale linear problem :param Aff: The fine scale assembled matrix :param ADff: The fine scale assembled matrix with fine Dirichlet boundary condition eliminated :param Bf: The fine scale assembled rhs :param BDf: The fine scale assembled rhs with fine Dirichlet boundary condition eliminated treated :param efunc: The enriched function functor that transform patch solution into a enriched function (i.g. shifting field to force enriched dof to be null) :param enriched_field: The field describing enriched field at coarse scale. The associate linear problem is managed by coarseManager object :type enriched_field: `Function <https://docs.fenicsproject.org/dolfinx/v0.9.0/python/generated/dolfinx.fem.html#dolfinx.fem.Function>`_ :param mpc: The multi point constraint, if any, used to create fine_space and created from a call to core.generateMPC with sj. :type mpc: `MultiPointConstraint <https://jsdokken.com/dolfinx_mpc/docs/api.html#dolfinx_mpc.MultiPointConstraint>`_ or None :param bc: The list of boundary conditions applied to system at coarse level :type bc: list of `DirichletBC <https://docs.fenicsproject.org/dolfinx/v0.9.0/python/generated/dolfinx.fem.html#dolfinx.fem.DirichletBC>`_ :param itmx: The maximum number of iteration to compute :type itmx: int :param eps: The target precision for relative residual value to be considered as null :type eps: floating point value :return: the field describing fine scale function related to fine scale resolution by the twoScale solver, the last residual computed the number of iteration done in the loop the history of residual evolution :rtype: tuple Function,float,int,list[float] """ # check coherance with mpc do_mpc = sj.needs_mpc if do_mpc and mpc is None: raise RuntimeError( "scale jump provided expect MPC creation and you provide no mpc !" ) # solution field at fine scale if do_mpc: solf = Function(mpc.function_space, name="sol_fine_ts") else: solf = Function(fine_space, name="sol_fine_ts") # generate coarseManager to deal with enriched problem at coarse scale stage = PETSc.Log.Stage("generateCM") stage.push() cm = core.generateCoarseManager( sj, fine_space, enriched_field.function_space, mpc, bc ) stage.pop() # generate patchManager to deal with patches problem at fine scale stage = PETSc.Log.Stage("generatePM") stage.push() pm = core.generatePatchManager(sj, fine_space) stage.pop() # generate patches problem from fine linear assembled system with Dirichlet BC eliminated stage = PETSc.Log.Stage("genprbPM") stage.push() pm.generateProblems(ADff, BDf) stage.pop() # generate standard part of the coarse enriched problem from fine linear assembled system stage = PETSc.Log.Stage("genstdCM") stage.push() cm.setStdCoarse(Aff, Bf) stage.pop() # obtaine PETSc solf xf = solf.x.petsc_vec # initialize twoscale loop by setting solf using standard part of the coarse enriched # field guess given by the user in enriched_field cm.projectStdCoarse(enriched_field, solf) # compute initial relative residual : |res|/|b| nb = BDf.norm() ra = residual(ADff, BDf, xf, 1.0) hrb = [ra / nb] # compute initial relative residual : |res|/|res0| nr = ra hrr = [1.0] # use max nm = max(nb, nr) epsr = nm * eps # enter the loop stage1 = PETSc.Log.Stage("solvePM") stage2 = PETSc.Log.Stage("resetCM") stage3 = PETSc.Log.Stage("PEupdateCM") stage4 = PETSc.Log.Stage("updateCM") stage5 = PETSc.Log.Stage("solveCM") stage6 = PETSc.Log.Stage("residual") i = 0 while i < itmx and ra > epsr: stage1.push() pm.solveProblems(solf) stage1.pop() stage2.push() cm.resetCoarseToStd() stage2.pop() stage3.push() cm.updateEnrichedOperator(pm, efunc) stage3.pop() stage4.push() cm.updateEnrichCoarse(Aff, Bf) stage4.pop() stage5.push() cm.solve(solf) stage5.pop() stage6.push() ra = residual(ADff, BDf, xf, 1.0) stage6.pop() hrb.append(ra / nb) hrr.append(ra / nr) i = i + 1 if do_mpc: solf.x.scatter_forward() mpc.backsubstitution(solf) return solf, ra, nm, i, hrb, hrr
# =================================================================
[docs] def linearBasicLoopEimp( sj: core.scaleJump, fine_space: FunctionSpace, Aff: PETSc.Mat, ADff: PETSc.Mat, Bf: PETSc.Vec, BDf: PETSc.Vec, efunc: core.enrichedFunction, enriched_field: Function, mpc: typing.Optional[MultiPointConstraint] = None, bc: list[DirichletBC] = [], itswitch=4, itmx=10, eps=1.0e-3, ): """ Function to solve a linear problem by looping in between scale with the twoscale approach At a specific iteration the enriched dofs are all set to 1 considering that enrichment functions are sufficiently representative of the phenomena so that enriched dof fluctuation can be ignored at coarse level. :note: This function can only be used when enrichment function is shifted because its only in this case that enriched dofs converge to one. :param sj: The scale jump describing the two scale :param fine_space: The space describing fine scale discretization related to fine scale linear problem :param Aff: The fine scale assembled matrix :param ADff: The fine scale assembled matrix with fine Dirichlet boundary condition eliminated :param Bf: The fine scale assembled rhs :param BDf: The fine scale assembled rhs with fine Dirichlet boundary condition eliminated treated :param efunc: The enriched function functor that transform patch solution into a enriched function (i.g. shifting field to force enriched dof to be null) :param enriched_field: The field describing enriched field at coarse scale. The associate linear problem is managed by coarseManager object :type enriched_field: `Function <https://docs.fenicsproject.org/dolfinx/v0.9.0/python/generated/dolfinx.fem.html#dolfinx.fem.Function>`_ :param mpc: The multi point constraint, if any, used to create fine_space and created from a call to core.generateMPC with sj. :type mpc: `MultiPointConstraint <https://jsdokken.com/dolfinx_mpc/docs/api.html#dolfinx_mpc.MultiPointConstraint>`_ or None :param itswitch: The maximum number of iteration to compute without imposing enriched dofs to one :type itswitch: int :param itmx: The maximum number of iteration to compute :type itmx: int :param eps: The target precision for relative residual value to be considered as null :type eps: floating point value :return: the field describing fine scale function related to fine scale resolution by the twoScale solver, the last residual computed the number of iteration done in the loop the history of residual evolution :rtype: tuple Function,float,int,list[float] """ # check coherance with mpc do_mpc = sj.needs_mpc if do_mpc and mpc is None: raise RuntimeError( "scale jump provided expect MPC creation and you provide no mpc !" ) # solution field at fine scale if do_mpc: solf = Function(mpc.function_space, name="sol_fine_ts") else: solf = Function(fine_space, name="sol_fine_ts") # generate coarseManager to deal with enriched problem at coarse scale stage = PETSc.Log.Stage("generateCMI") stage.push() cm = core.generateCoarseManager( sj, fine_space, enriched_field.function_space, mpc, bc ) stage.pop() # generate patchManager to deal with patches problem at fine scale stage = PETSc.Log.Stage("generatePMI") stage.push() pm = core.generatePatchManager(sj, fine_space) stage.pop() # generate patches problem from fine linear assembled system with Dirichlet BC eliminated stage = PETSc.Log.Stage("genprbPMI") stage.push() pm.generateProblems(ADff, BDf) stage.pop() # generate standard part of the coarse enriched problem from fine linear assembled system stage = PETSc.Log.Stage("genstdCMI") stage.push() cm.setStdCoarse(Aff, Bf, True) stage.pop() # obtaine PETSc solf xf = solf.x.petsc_vec # initialize twoscale loop by setting solf using standard part of the coarse enriched # field guess given by the user in enriched_field cm.projectStdCoarse(enriched_field, solf) # compute initial relative residual : |res|/|b| nb = BDf.norm() ra = residual(ADff, BDf, xf, 1.0) hrb = [ra / nb] # compute initial relative residual : |res|/|res0| nr = ra hrr = [1.0] # use max nm = max(nb, nr) epsr = nm * eps # enter the loop stage1 = PETSc.Log.Stage("solvePMI") stage2 = PETSc.Log.Stage("resetCMI") stage3 = PETSc.Log.Stage("PEupdateCMI") stage4 = PETSc.Log.Stage("updateCMI") stage5 = PETSc.Log.Stage("solveCMI") stage6 = PETSc.Log.Stage("residualI") stage7 = PETSc.Log.Stage("updateEImpCMI") stage8 = PETSc.Log.Stage("solveEImpCMI") i = 0 while i < itmx and ra > epsr: stage1.push() pm.solveProblems(solf) stage1.pop() stage2.push() cm.resetCoarseToStd() stage2.pop() stage3.push() cm.updateEnrichedOperator(pm, efunc) stage3.pop() if i > itswitch: stage7.push() cm.updateEImp() stage7.pop() stage8.push() cm.solveEImp(solf) stage8.pop() else: stage4.push() cm.updateEnrichCoarse(Aff, Bf) stage4.pop() stage5.push() cm.solve(solf) stage5.pop() stage6.push() ra = residual(ADff, BDf, xf, 1.0) stage6.pop() hrb.append(ra / nb) hrr.append(ra / nr) i = i + 1 if do_mpc: solf.x.scatter_forward() mpc.backsubstitution(solf) return solf, ra, nm, i, hrb, hrr
# =================================================================
[docs] def linearBasicLoopVideo( filename: str, scale, sj: core.scaleJump, fine_space: FunctionSpace, Aff: PETSc.Mat, ADff: PETSc.Mat, Bf: PETSc.Vec, BDf: PETSc.Vec, efunc: core.enrichedFunction, enriched_field: Function, mpc: typing.Optional[MultiPointConstraint] = None, bc: list[DirichletBC] = [], itmx=10, eps=1.0e-3, camera=None, patch=False, ): """ Function to solve a linear problem by looping in between scale with the twoscale approach It works like linearBasicLoop but generate a gif movie file and thus should only be used for debugging or ilustration but not for real life application. :param filename: The name of the gif file to create :param scale: scaling factor of the deformed mesh by the field :param sj: The scale jump describing the two scale :param fine_space: The space describing fine scale discretization related to fine scale linear problem :param Aff: The fine scale assembled matrix :param ADff: The fine scale assembled matrix with fine Dirichlet boundary condition eliminated :param Bf: The fine scale assembled rhs :param BDf: The fine scale assembled rhs with fine Dirichlet boundary condition eliminated treated :param efunc: The enriched function functor that transform patch solution into a enriched function (i.g. shifting field to force enriched dof to be null) :param enriched_field: The field describing enriched field at coarse scale. The associate linear problem is managed by coarseManager object :type enriched_field: `Function <https://docs.fenicsproject.org/dolfinx/v0.9.0/python/generated/dolfinx.fem.html#dolfinx.fem.Function>`_ :param mpc: The multi point constraint, if any, used to create fine_space and created from a call to core.generateMPC with sj. :type mpc: `MultiPointConstraint <https://jsdokken.com/dolfinx_mpc/docs/api.html#dolfinx_mpc.MultiPointConstraint>`_ or None :param itmx: The maximum number of iteration to compute :type itmx: int :param eps: The target precision for relative residual value to be considered as null :type eps: floating point value :return: the field describing fine scale function related to fine scale resolution by the twoScale solver, the last residual computed the number of iteration done in the loop :rtype: tuple Function,float,int """ # check coherance with mpc do_mpc = sj.needs_mpc if do_mpc and mpc is None: raise RuntimeError( "scale jump provided expect MPC creation and you provide no mpc !" ) # solution field at fine scale if do_mpc: solf = Function(mpc.function_space, name="sol_fine_ts") ds = Function(mpc.function_space, name="delta_sol_fine_ts") if patch: patch_field = Function(mpc.function_space, name="patches") else: solf = Function(fine_space, name="sol_fine_ts") ds = Function(fine_space, name="delta_sol_fine_ts") if patch: patch_field = Function(fine_space, name="patches") # generate coarseManager to deal with enriched problem at coarse scale stage = PETSc.Log.Stage("generateCMV") stage.push() cm = core.generateCoarseManager( sj, fine_space, enriched_field.function_space, mpc, bc ) stage.pop() # generate patchManager to deal with patches problem at fine scale stage = PETSc.Log.Stage("generatePMV") stage.push() pm = core.generatePatchManager(sj, fine_space) stage.pop() # generate patches problem from fine linear assembled system with Dirichlet BC eliminated pm.generateProblems(ADff, BDf) # for patches output if patch: list_seq = range(pm.numberOfSequence) domain = sj.getFineMesh dim = domain.topology.dim topoc = sj.getCoarseMesh.topology adj = topoc.connectivity(0, dim) # generate standard part of the coarse enriched problem from fine linear assembled system cm.setStdCoarse(Aff, Bf) # obtaine PETSc solf xf = solf.x.petsc_vec dxf = ds.x.petsc_vec # initialize twoscale loop by setting solf using standard part of the coarse enriched # field guess given by the user in enriched_field cm.projectStdCoarse(enriched_field, solf) # compute initial relative residual : |res|/|b| nb = BDf.norm() ra = residual(ADff, BDf, xf, 1.0) hrb = [ra / nb] # compute initial relative residual : |res|/|res0| nr = ra hrr = [1.0] # compute initial relative variation : |du|/|u0| nd = np.sqrt(xf.dot(xf)) rds = 1.0 hds = [rds] ds.x.array[:] = -solf.x.array # use max nm = max(nb, nr) epsr = nm * eps master = enriched_field.function_space.mesh.comm.rank == 0 if do_mpc: solf.x.scatter_forward() mpc.backsubstitution(solf) warped = util.root_field(solf, scale) if do_mpc: mpc.homogenize(solf) if master: if patch: plt = pv.Plotter(shape=(1, 3)) else: plt = pv.Plotter(shape=(1, 2)) plt.open_gif(filename, fps=1) plt.subplot(0, 1) chart = pv.Chart2D() chart.line(range(len(hrb)), hrb, label="|res|/|b|") chart.scatter(range(len(hrb)), hrb) chart.line(range(len(hrr)), hrr, color="#F5B027", label="|res|/|res_0|") chart.scatter(range(len(hrr)), hrr, color="#F5B027") chart.line(range(len(hds)), hds, color="#D41E0F", label="|ds|/|s|") chart.scatter(range(len(hds)), hds, color="#D41E0F") chart.x_label = "iterations" chart.y_label = "Relative residual" plt.add_chart(chart) plt.subplot(0, 0) plt.add_mesh(warped, show_edges=True, show_scalar_bar=True) plt.add_title("iter 0: {}".format(ra / nm), font_size=8) if camera is not None: plt.camera_position = camera if patch: plt.subplot(0, 2) else: plt.write_frame() if patch: pm.solveProblems(solf) for seq in list_seq: idp = pm.grabPatchSolution(seq, patch_field) patch_field.x.scatter_forward() if do_mpc: mpc.backsubstitution(patch_field) # childs cells of the support of enriched nodes if idp > -1: cells = [] for el in adj.links(idp): cells.append(sj.getChildren(el)) cells = np.sort(np.concatenate(cells)) else: cells = np.empty(0, dtype=int) # submesh of the support sdom = mesh.create_submesh(domain, dim, cells) space_patch = functionspace( sdom[0], sdom[0].ufl_domain().ufl_coordinate_element() ) field_patch = Function(space_patch) cell_over = np.arange( sdom[0].topology.index_map(dim).size_local, dtype=int ) link = create_interpolation_data(space_patch, fine_space, cell_over) field_patch.interpolate_nonmatching(patch_field, cell_over, link) warpedf = util.root_field(field_patch, scale) if master: plt.add_mesh(warpedf, show_edges=True, show_scalar_bar=True) if master: if camera is not None: plt.camera_position = camera plt.write_frame() # enter the loop i = 0 stage1 = PETSc.Log.Stage("solvePMV") stage2 = PETSc.Log.Stage("resetCMV") stage3 = PETSc.Log.Stage("PEupdateCMV") stage4 = PETSc.Log.Stage("updateCMV") stage5 = PETSc.Log.Stage("solveCMV") stage6 = PETSc.Log.Stage("residualV") if master: print(f"At twoscale iteration {i} residual is {ra / nb}(b) 1.(r) 1.(ds)") while i < itmx and ra > epsr and rds > eps / 100: stage1.push() pm.solveProblems(solf) stage1.pop() stage2.push() cm.resetCoarseToStd() stage2.pop() stage3.push() cm.updateEnrichedOperator(pm, efunc) stage3.pop() stage4.push() cm.updateEnrichCoarse(Aff, Bf) stage4.pop() stage5.push() cm.solve(solf) stage5.pop() stage6.push() ra = residual(ADff, BDf, xf, 1.0) stage6.pop() ds.x.array[:] += solf.x.array rds = np.sqrt(dxf.dot(dxf)) / nd hrb.append(ra / nb) hrr.append(ra / nr) hds.append(rds) ds.x.array[:] = -solf.x.array i = i + 1 if do_mpc and True: solf.x.scatter_forward() mpc.backsubstitution(solf) warped = util.root_field(solf, scale) if do_mpc and True: mpc.homogenize(solf) if master: print( f"At twoscale iteration {i} residual is {ra / nb}(b) {ra / nr}(r) {rds}(ds)" ) plt.clear() plt.subplot(0, 1) chart = pv.Chart2D() chart.line(range(len(hrb)), hrb, label="|res|/|b|") chart.scatter(range(len(hrb)), hrb) chart.line(range(len(hrr)), hrr, color="#F5B027", label="|res|/|res_0|") chart.scatter(range(len(hrr)), hrr, color="#F5B027") chart.line(range(len(hds)), hds, color="#D41E0F", label="|ds|/|s|") chart.scatter(range(len(hds)), hds, color="#D41E0F") chart.x_label = "iterations" chart.y_label = "Criterion" chart.y_axis.log_scale = True plt.add_chart(chart) plt.subplot(0, 0) plt.add_mesh(warped, show_edges=True, show_scalar_bar=True) plt.add_title("iter {}: {}".format(i, ra / nm), font_size=8) if camera is not None: plt.camera_position = camera if patch: plt.subplot(0, 2) else: plt.write_frame() if patch: for seq in list_seq: idp = pm.grabPatchSolution(seq, patch_field) patch_field.x.scatter_forward() if do_mpc: mpc.backsubstitution(patch_field) # childs cells of the support of enriched nodes if idp > -1: cells = [] for el in adj.links(idp): cells.append(sj.getChildren(el)) cells = np.sort(np.concatenate(cells)) else: cells = np.empty(0, dtype=int) # submesh of the support sdom = mesh.create_submesh(domain, dim, cells) space_patch = functionspace( sdom[0], sdom[0].ufl_domain().ufl_coordinate_element() ) field_patch = Function(space_patch) cell_over = np.arange( sdom[0].topology.index_map(dim).size_local, dtype=int ) link = create_interpolation_data(space_patch, fine_space, cell_over) field_patch.interpolate_nonmatching(patch_field, cell_over, link) warpedf = util.root_field(field_patch, scale) if master: plt.add_mesh(warpedf, show_edges=True, show_scalar_bar=True) if master: if camera is not None: plt.camera_position = camera plt.write_frame() if master: plt.close() if do_mpc: # xf.view() solf.x.scatter_forward() mpc.backsubstitution(solf) # xf.view() return solf, ra / nm, i, pm, cm, hrb, hrr, hds
# =================================================================
[docs] def initLinearBasicLoop( sj: core.scaleJump, A: PETSc.Mat, B: PETSc.Vec, fine_space: FunctionSpace, enriched_field: Function, mpc: typing.Optional[MultiPointConstraint] = None, bc: list[DirichletBC] = [], ): """ Function to initialize twoscale object used in runLinearBasicLoop :param sj: The scale jump describing the two scale :param A: The fine scale assembled matrix (it can be either with Dirichlet boundary condition eliminated or not) :param B: The fine scale assembled rhs (it can be either with Dirichlet boundary condition eliminated or not) :param fine_space: The space describing fine scale discretization related to fine scale linear problem :param enriched_field: The field describing enriched field at coarse scale. The associate linear problem is managed by coarseManager object :type enriched_field: `Function <https://docs.fenicsproject.org/dolfinx/v0.9.0/python/generated/dolfinx.fem.html#dolfinx.fem.Function>`_ :param mpc: The multi point constraint, if any, used to create fine_space and created from a call to core.generateMPC with sj. :type mpc: `MultiPointConstraint <https://jsdokken.com/dolfinx_mpc/docs/api.html#dolfinx_mpc.MultiPointConstraint>`_ or None :return: The field describing fine scale function related to fine scale resolution by the twoScale solver, the coarseManager object to use for the resolution and the patchManager object to use for the resolution :rtype: tuple Function,coarseManager,patchManager :warning: A and B must be both with Dirichlet boundary condition eliminated or not. Mixing eliminated and not eliminated is an error """ # check coherance with mpc do_mpc = sj.needs_mpc if do_mpc and mpc is None: raise RuntimeError( "scale jump provided expect MPC creation and you provide no mpc !" ) # solution field at fine scale if do_mpc: solf = Function(mpc.function_space, name="sol_fine_ts") else: solf = Function(fine_space, name="sol_fine_ts") # generate coarseManager to deal with enriched problem at coarse scale cm = core.generateCoarseManager( sj, fine_space, enriched_field.function_space, mpc, bc ) # generate patchManager to deal with patches problem at fine scale pm = core.generatePatchManager(sj, fine_space) # generate patches problem from fine linear assembled system with Dirichlet BC eliminated pm.generateProblems(A, B) return solf, cm, pm
# =================================================================
[docs] def runLinearBasicLoop( solf: Function, cm: core.coarseManager, pm: core.patchManager, Aff: PETSc.Mat, ADff: PETSc.Mat, Bf: PETSc.Vec, BDf: PETSc.Vec, efunc: core.enrichedFunction, enriched_field: Function, mpc: typing.Optional[MultiPointConstraint] = None, itmx=10, eps=1.0e-3, reset_sol=True, ): """ Function to solve a linear problem by looping in between scale with the twoscale approach :param solf: The field describing fine scale function related to fine scale resolution by the twoScale solver :param cm: coarseManager object initialized by initLinearBasicLoop function :type cm: twoscale.core.coarseManager object :param pm: patchManager object initialized by initLinearBasicLoop function :type pm: twoscale.core.patchManager object :param Aff: The fine scale assembled matrix :param ADff: The fine scale assembled matrix with fine Dirichlet boundary condition eliminated :param Bf: The fine scale assembled rhs :param BDf: The fine scale assembled rhs with fine Dirichlet boundary condition eliminated treated :param efunc: The enriched function functor that transform patch solution into a enriched function (i.g. shifting field to force enriched dof to be null) :param enriched_field: The field describing enriched field at coarse scale. The associate linear problem is managed by coarseManager object :type enriched_field: `Function <https://docs.fenicsproject.org/dolfinx/v0.9.0/python/generated/dolfinx.fem.html#dolfinx.fem.Function>`_ :param mpc: The multi point constraint, if any, used to create fine_space and created from a call to core.generateMPC with sj. :type mpc: `MultiPointConstraint <https://jsdokken.com/dolfinx_mpc/docs/api.html#dolfinx_mpc.MultiPointConstraint>`_ or None :param itmx: The maximum number of iteration to compute :type itmx: int :param eps: The target precision for relative residual value to be considered as null :type eps: floating point value :param reset_sol: Reset the solution field using enrichied_fied by projection with standard operator :return: the last min residual computed and the number of iteration done in the loop :rtype: tuple float,int """ # generate standard part of the coarse enriched problem from fine linear assembled system cm.setStdCoarse(Aff, Bf) # obtaine PETSc solf xf = solf.x.petsc_vec if reset_sol: # initialize twoscale loop by setting solf using standard part of the coarse enriched # field guess given by the user in enriched_field cm.projectStdCoarse(enriched_field, solf) # compute initial relative residual : |res|/|b| nb = BDf.norm() ra = residual(ADff, BDf, xf, 1.0) hrb = [ra / nb] # compute initial relative residual : |res|/|res0| nr = ra hrr = [1.0] # use max nm = max(nb, nr) epsr = nm * eps # enter the loop i = 0 while i < itmx and ra > epsr: pm.solveProblems(solf) cm.resetCoarseToStd() cm.updateEnrichedOperator(pm, efunc) cm.updateEnrichCoarse(Aff, Bf) cm.solve(solf) ra = residual(ADff, BDf, xf, 1.0) hrb.append(ra / nb) hrr.append(ra / nr) i = i + 1 if mpc is not None: solf.x.scatter_forward() mpc.backsubstitution(solf) return ra / nm, i, hrb, hrr
# =================================================================
[docs] def runLinearBasicLoopEImp( solf: Function, cm: core.coarseManager, pm: core.patchManager, Aff: PETSc.Mat, ADff: PETSc.Mat, Bf: PETSc.Vec, BDf: PETSc.Vec, efunc: core.enrichedFunction, enriched_field: Function, mpc: typing.Optional[MultiPointConstraint] = None, itswitch=4, itmx=10, eps=1.0e-3, reset_sol=True, ): """ Function to solve a linear problem by looping in between scale with the twoscale approach At a specific iteration the enriched dofs are all set to 1 considering that enrichment functions are sufficiently representative of the phenomena so that enriched dof fluctuation can be ignored at coarse level. :note: This function can only be used when enrichment function is shifted because its only in this case that enriched dofs converge to one. :param solf: The field describing fine scale function related to fine scale resolution by the twoScale solver :param cm: coarseManager object initialized by initLinearBasicLoop function :type cm: twoscale.core.coarseManager object :param pm: patchManager object initialized by initLinearBasicLoop function :type pm: twoscale.core.patchManager object :param Aff: The fine scale assembled matrix :param ADff: The fine scale assembled matrix with fine Dirichlet boundary condition eliminated :param Bf: The fine scale assembled rhs :param BDf: The fine scale assembled rhs with fine Dirichlet boundary condition eliminated treated :param efunc: The enriched function functor that transform patch solution into a enriched function (i.g. shifting field to force enriched dof to be null) :param enriched_field: The field describing enriched field at coarse scale. The associate linear problem is managed by coarseManager object :type enriched_field: `Function <https://docs.fenicsproject.org/dolfinx/v0.9.0/python/generated/dolfinx.fem.html#dolfinx.fem.Function>`_ :param mpc: The multi point constraint, if any, used to create fine_space and created from a call to core.generateMPC with sj. :type mpc: `MultiPointConstraint <https://jsdokken.com/dolfinx_mpc/docs/api.html#dolfinx_mpc.MultiPointConstraint>`_ or None :param itswitch: The maximum number of iteration to compute without imposing enriched dofs to one :type itswitch: int :param itmx: The maximum number of iteration to compute :type itmx: int :param eps: The target precision for relative residual value to be considered as null :type eps: floating point value :param reset_sol: Reset the solution field using enrichied_fied by projection with standard operator :return: the last min residual computed and the number of iteration done in the loop :rtype: tuple float,int """ # generate standard part of the coarse enriched problem from fine linear assembled system # and generate also information for imposed enriched dofs operations cm.setStdCoarse(Aff, Bf, True) # obtaine PETSc solf xf = solf.x.petsc_vec if reset_sol: # initialize twoscale loop by setting solf using standard part of the coarse enriched # field guess given by the user in enriched_field cm.projectStdCoarse(enriched_field, solf) # compute initial relative residual : |res|/|b| nb = BDf.norm() ra = residual(ADff, BDf, xf, 1.0) hrb = [ra / nb] # compute initial relative residual : |res|/|res0| nr = ra hrr = [1.0] # use max nm = max(nb, nr) epsr = nm * eps # enter the loop i = 0 while i < itmx and ra > epsr: pm.solveProblems(solf) cm.resetCoarseToStd() cm.updateEnrichedOperator(pm, efunc) if i > itswitch: cm.updateEImp() cm.solveEImp(solf) else: cm.updateEnrichCoarse(Aff, Bf) cm.solve(solf) ra = residual(ADff, BDf, xf, 1.0) hrb.append(ra / nb) hrr.append(ra / nr) i = i + 1 if mpc is not None: solf.x.scatter_forward() mpc.backsubstitution(solf) return ra / nm, i, hrb, hrr