# This file was automatically generated by SWIG (https://www.swig.org).
# Version 4.1.1
#
# Do not make changes to this file unless you know what you are doing - modify
# the SWIG interface file instead.

from sys import version_info as _swig_python_version_info
# Import the low-level C/C++ module
if __package__ or "." in __name__:
    from . import _csubproblem
else:
    import _csubproblem

try:
    import builtins as __builtin__
except ImportError:
    import __builtin__

def _swig_repr(self):
    try:
        strthis = "proxy of " + self.this.__repr__()
    except __builtin__.Exception:
        strthis = ""
    return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)


def _swig_setattr_nondynamic_instance_variable(set):
    def set_instance_attr(self, name, value):
        if name == "this":
            set(self, name, value)
        elif name == "thisown":
            self.this.own(value)
        elif hasattr(self, name) and isinstance(getattr(type(self), name), property):
            set(self, name, value)
        else:
            raise AttributeError("You cannot add instance attributes to %s" % self)
    return set_instance_attr


def _swig_setattr_nondynamic_class_variable(set):
    def set_class_attr(cls, name, value):
        if hasattr(cls, name) and not isinstance(getattr(cls, name), property):
            set(cls, name, value)
        else:
            raise AttributeError("You cannot add class attributes to %s" % cls)
    return set_class_attr


def _swig_add_metaclass(metaclass):
    """Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass"""
    def wrapper(cls):
        return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy())
    return wrapper


class _SwigNonDynamicMeta(type):
    """Meta class to enforce nondynamic attributes (no new attributes) for a class"""
    __setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__)


import ooflib.SWIG.common.coord
import ooflib.SWIG.common.doublevec
import ooflib.SWIG.common.lock
import ooflib.SWIG.engine.element
import ooflib.SWIG.engine.edge
import ooflib.SWIG.engine.field
import ooflib.SWIG.engine.femesh
import ooflib.SWIG.common.cmicrostructure
import ooflib.SWIG.common.pixelgroup
import ooflib.SWIG.common.pixelattribute
import ooflib.SWIG.common.timestamp
import ooflib.SWIG.engine.node
import ooflib.SWIG.engine.pointdata
import ooflib.SWIG.engine.material
import ooflib.SWIG.common.abstractimage
import ooflib.SWIG.common.ccolor
import ooflib.SWIG.engine.property
import ooflib.SWIG.engine.meshiterator
import ooflib.SWIG.engine.fieldindex
import ooflib.SWIG.engine.planarity
import ooflib.SWIG.engine.freedom
import ooflib.SWIG.engine.outputval
import ooflib.SWIG.engine.mastercoord
import ooflib.SWIG.engine.elementnodeiterator
import ooflib.SWIG.engine.gausspoint
import ooflib.SWIG.engine.equation
import ooflib.SWIG.engine.flux
import ooflib.SWIG.engine.nodalequation
import ooflib.SWIG.engine.linearizedsystem
import ooflib.SWIG.engine.sparsemat
class CSubProblem(object):
    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")

    def __init__(self, *args, **kwargs):
        raise AttributeError("No constructor defined")
    __repr__ = _swig_repr
    __swig_destroy__ = _csubproblem.delete_CSubProblem

    def set_rwlock(self, rw):
        return _csubproblem.CSubProblem_set_rwlock(self, rw)

    def get_rwlock(self):
        return _csubproblem.CSubProblem_get_rwlock(self)

    def set_femesh(self, arg2):
        return _csubproblem.CSubProblem_set_femesh(self, arg2)

    def get_mesh(self):
        return _csubproblem.CSubProblem_get_mesh(self)

    def set_nnodes(self, arg2):
        return _csubproblem.CSubProblem_set_nnodes(self, arg2)

    def ndof(self):
        return _csubproblem.CSubProblem_ndof(self)

    def neqn(self):
        return _csubproblem.CSubProblem_neqn(self)

    def containsNode(self, arg2):
        return _csubproblem.CSubProblem_containsNode(self, arg2)
    precomputeLock = property(_csubproblem.CSubProblem_precomputeLock_get, _csubproblem.CSubProblem_precomputeLock_set)
    precomputeRequired = property(_csubproblem.CSubProblem_precomputeRequired_get, _csubproblem.CSubProblem_precomputeRequired_set)

    def requirePrecompute(self):
        return _csubproblem.CSubProblem_requirePrecompute(self)

    def redefined(self):
        return _csubproblem.CSubProblem_redefined(self)

    def nodes(self):
        return _csubproblem.CSubProblem_nodes(self)

    def funcnodes(self):
        return _csubproblem.CSubProblem_funcnodes(self)

    def elements(self):
        return _csubproblem.CSubProblem_elements(self)

    def new_linear_system(self, arg2):
        return _csubproblem.CSubProblem_new_linear_system(self, arg2)

    def make_linear_system(self, arg2, arg3):
        return _csubproblem.CSubProblem_make_linear_system(self, arg2, arg3)

    def all_compound_fields(self):
        return _csubproblem.CSubProblem_all_compound_fields(self)

    def all_equations(self):
        return _csubproblem.CSubProblem_all_equations(self)

    def all_fluxes(self):
        return _csubproblem.CSubProblem_all_fluxes(self)

    def getMaterials(self):
        return _csubproblem.CSubProblem_getMaterials(self)

    def define_field(self, arg2):
        return _csubproblem.CSubProblem_define_field(self, arg2)

    def undefine_field(self, arg2):
        return _csubproblem.CSubProblem_undefine_field(self, arg2)

    def is_defined_field(self, arg2):
        return _csubproblem.CSubProblem_is_defined_field(self, arg2)

    def activate_field(self, arg2):
        return _csubproblem.CSubProblem_activate_field(self, arg2)

    def deactivate_field(self, arg2):
        return _csubproblem.CSubProblem_deactivate_field(self, arg2)

    def is_active_field(self, arg2):
        return _csubproblem.CSubProblem_is_active_field(self, arg2)

    def n_active_fields(self):
        return _csubproblem.CSubProblem_n_active_fields(self)

    def acquire_field_data(self, field, arg3):
        return _csubproblem.CSubProblem_acquire_field_data(self, field, arg3)

    def is_active_flux(self, arg2):
        return _csubproblem.CSubProblem_is_active_flux(self, arg2)

    def activate_equation(self, arg2):
        return _csubproblem.CSubProblem_activate_equation(self, arg2)

    def deactivate_equation(self, arg2):
        return _csubproblem.CSubProblem_deactivate_equation(self, arg2)

    def is_active_equation(self, arg2):
        return _csubproblem.CSubProblem_is_active_equation(self, arg2)

    def n_active_eqns(self):
        return _csubproblem.CSubProblem_n_active_eqns(self)

    def setStaticStepper(self, arg2):
        return _csubproblem.CSubProblem_setStaticStepper(self, arg2)

    def staticStepper(self):
        return _csubproblem.CSubProblem_staticStepper(self)

    def mapFields(self):
        return _csubproblem.CSubProblem_mapFields(self)

    def set_meshdofs(self, arg2):
        return _csubproblem.CSubProblem_set_meshdofs(self, arg2)

    def get_meshdofs(self):
        return _csubproblem.CSubProblem_get_meshdofs(self)

    def set_slaveDoF(self, arg2):
        return _csubproblem.CSubProblem_set_slaveDoF(self, arg2)

    def clear_slaveDoFs(self):
        return _csubproblem.CSubProblem_clear_slaveDoFs(self)

    def post_process(self):
        return _csubproblem.CSubProblem_post_process(self)

    def set_equation_mapping(self, arg2):
        return _csubproblem.CSubProblem_set_equation_mapping(self, arg2)

    def init_scpatches(self, arg2):
        return _csubproblem.CSubProblem_init_scpatches(self, arg2)

    def add_scpatch(self, arg2, arg3, arg4, arg5, arg6, arg7):
        return _csubproblem.CSubProblem_add_scpatch(self, arg2, arg3, arg4, arg5, arg6, arg7)

    def get_elements_from_patch(self, arg2, arg3):
        return _csubproblem.CSubProblem_get_elements_from_patch(self, arg2, arg3)

    def get_nodes_from_patch(self, arg2, arg3):
        return _csubproblem.CSubProblem_get_nodes_from_patch(self, arg2, arg3)

    def init_nodalfluxes(self):
        return _csubproblem.CSubProblem_init_nodalfluxes(self)

    def recover_fluxes(self):
        return _csubproblem.CSubProblem_recover_fluxes(self)

    def report_recovered_fluxes(self, arg2, Point):
        return _csubproblem.CSubProblem_report_recovered_fluxes(self, arg2, Point)

    def zz_L2_estimate(self, arg2, arg3):
        return _csubproblem.CSubProblem_zz_L2_estimate(self, arg2, arg3)

    def zz_L2_weights(self, arg2, arg3, arg4):
        return _csubproblem.CSubProblem_zz_L2_weights(self, arg2, arg3, arg4)

    def contains(self, arg2):
        return _csubproblem.CSubProblem_contains(self, arg2)

    def __hash__(self):
        return _csubproblem.CSubProblem___hash__(self)

# Register CSubProblem in _csubproblem:
_csubproblem.CSubProblem_swigregister(CSubProblem)

def get_globalCSubProblemCount():
    return _csubproblem.get_globalCSubProblemCount()
# -*- python -*-

# This software was produced by NIST, an agency of the U.S. government,
# and by statute is not subject to copyright in the United States.
# Recipients of this software assume all responsibilities associated
# with its operation, modification and maintenance. However, to
# facilitate maintenance we ask that before distributing modified
# versions of this software, you first contact the authors at
# oof_manager@nist.gov. 

from ooflib.SWIG.common import config
from ooflib.common import debug
from ooflib.engine import scpatch

def _set_mesh(self, meshctxt):
# TODO: get rid of self.mesh, which is redundant with
# CSubProblem.get_mesh(), which is the better thing to
# use. (Different swig-wrapped CSubProblems can refer to the same
# C++ CSubProblem, but mesh and meshctxt will be set in only one
# of the wrapped CSubProblems.  This is a issue inside
# pypropertywrapper functions.)  The property code below may be
# useful.  We probably need to get rid of self.meshcontext as
# well, which may mean storing it in C++ as a PyObject*.
    self.meshcontext = meshctxt
    if  meshctxt is not None:
        self.mesh = meshctxt.getObject()
        self.set_femesh(self.mesh)
    else:
        self.mesh = None
CSubProblem.set_mesh = _set_mesh

## Code that may be useful when getting rid of self.mesh.
# def _mesh_setter(self, meshobj):
#     raise ooferror.ErrPyProgrammingError("Don't set CSubProblem.mesh!")
# def _mesh_getter(self):
#     return self.get_femesh()
# CSubProblem.mesh = property(_mesh_getter, _mesh_setter)


# Return the paths to the other subproblems on which this subproblem
# depends.  This function must be overridden in derived classes.
def _get_dependencies(self):
    return []
CSubProblem.get_dependencies = _get_dependencies


# For every defined flux, check that it is consistently defined.
# Returns a list of badly defined materials.
def _check_materials(self):
    badmatls = set()
    for f in self.all_fluxes():
        if self.is_active_flux(f):
# check_flux returns a list of bad materials
            badmatls.update(self.check_flux(f))
    return list(badmatls)
CSubProblem.check_materials = _check_materials


# Check if the passed-in flux is computable on this mesh.  Return a
# list of materials that make the flux incomputable, if any.
def _check_flux(self, flux):
    matlist = []
    for mat in self.getMaterials():
# mat can be None if there are elements with no assigned material
        if mat and mat.contributes_to_flux(flux):
            if not mat.self_consistent():
                matlist.append(mat)
    return matlist
CSubProblem.check_flux = _check_flux

# Run through the list of active equations, and make sure that there
# is a material which contributes to them.  Returns a list of bad
# equations.  This doesn't have to check equations that may have been
# implicitly defined at a Node, since they've been put there by a
# Material Property.
## TODO: This isn't sufficient.  There needs to be a contribution to
## the highest derivative (?) term in the equation from all Materials.
## If solving the heat equation and some material have no thermal
## conductivity, the matrices will not be square.

def _check_equations(self):
    bad_eqns = []
    for e in self.all_equations():
        if self.is_active_equation(e):
## TODO: This only applies to FluxEquations
            flx = e.flux()
            for mat in self.getMaterials():
                if mat and mat.contributes_to_flux(flx):
                    break
            else:
                bad_eqns.append(e)
    return bad_eqns
CSubProblem.check_equations = _check_equations

def _nEquations(self):
    n = 0
    for e in self.all_equations():
        if self.is_active_equation(e):
            n += e.ndof()
    return n
CSubProblem.nEquations = _nEquations

def _nDoFs(self):
    n = 0
    for f in self.all_compound_fields():
        if self.is_active_field(f):
            n += f.ndof()
        if self.is_active_field(f.out_of_plane()):
            n += f.out_of_plane().ndof()
    return n
CSubProblem.nDoFs = _nDoFs

###############################################################
#AMR subproblem

#Add attributes that used to be in femesh
CSubProblem.bdy_node_map = {}  # node_index: 0 or 1(on boundary)
# SCPatch flags

#Interface branch
#Retrieve realmesh nodes between the two realmesh corner nodes
#with indices into internal list irealn0 and irealn1 of the realmesh element realel.
#Note the difference with getEdgeNodes, which uses a dictionary and does not
#work when interfaces are defined.
def _CSubProblem_getRealEdgeNodes(self,realel,
                                     irealn0,realn0,
                                     irealn1,realn1):
    ncorners = realel.ncorners()
    found=False
    realedgenodes=[]
    if (irealn0+1)%ncorners==irealn1:
        for rn in realel.node_iterator():
            if found:
                if rn==realn1:
                    break
                realedgenodes.append(rn)
            if rn==realn0:
                found=True
    else:
        for rn in realel.node_iterator():
            if found:
                if rn==realn0:
                    break
                realedgenodes.append(rn)
            if rn==realn1:
                found=True
    return realedgenodes

CSubProblem.getRealEdgeNodes = _CSubProblem_getRealEdgeNodes

# Create a boundary node map, which indicates if a node is on a
# boundary (either external or material) or not.  The map is a dict
# whose keys are the indices of the nodes in the FEMesh, and whose
# values are 0 (for non-boundary nodes) and 1 (for boundary nodes).
# The map is a dict and not a list because mesh node indices aren't
# guaranteed to be a set of contiguous integers.

def _CSubProblem_create_elem_bdynodemap(self, e, fe, n0, n1):
    irealn0 = e.getNodeIndexIntoList(n0)
    realn0 = fe.getCornerNode( irealn0 )
    irealn1 = e.getNodeIndexIntoList(n1)
    realn1 = fe.getCornerNode( irealn1 )
    self.bdy_node_map[realn0.index()] = 1
    self.bdy_node_map[realn1.index()] = 1
    if self.mesh.order() > 1:
#Interface branch
        for nd in self.getRealEdgeNodes(fe,
                                        irealn0,realn0,
                                        irealn1,realn1):
            self.bdy_node_map[nd.index()] = 1
CSubProblem.create_elem_bdynodemap=_CSubProblem_create_elem_bdynodemap

#Interface branch
def _CSubProblem_create_bdy_node_map(self, skel):
# Initializing
    for node in self.nodes():
        self.bdy_node_map[node.index()] = 0

    for seg in skel.segment_iterator():
        n0, n1 = seg.nodes()
        if seg.nElements() == 1:  # geometric (external) boundary
#Interface branch
            e0 = seg.getElements()[0]
            fe0 = self.mesh.getElement(e0.meshindex)
#AMR subproblem
            if self.contains(fe0):
#realmesh element is in this subproblem
                self.create_elem_bdynodemap(e0,fe0,n0,n1)
##            irealn0 = e0.getNodeIndexIntoList(n0)
##            realn0 = fe0.getCornerNode( irealn0 )
##            irealn1 = e0.getNodeIndexIntoList(n1)
##            realn1 = fe0.getCornerNode( irealn1 )
##            #Get the index from realmesh nodes
##            #self.bdy_node_map[n0.meshindex] = 1
##            #self.bdy_node_map[n1.meshindex] = 1
##            self.bdy_node_map[realn0.index()] = 1
##            self.bdy_node_map[realn1.index()] = 1
##            if self.order() > 1:
##                #Interface branch
##                #for nd in self.getEdgeNodes((n0,n1)):
##                for nd in self.getRealEdgeNodes(fe0,
##                                                irealn0,realn0,
##                                                irealn1,realn1):
##                    self.bdy_node_map[nd.index()] = 1
        else:
            e0, e1 = seg.getElements()
            fe0 = self.mesh.getElement(e0.meshindex)
            fe1 = self.mesh.getElement(e1.meshindex)
            if self.contains(fe0)and self.contains(fe1):
#AMR subproblem, do this if fe0 and fe1 are both in the subproblem
#Interface branch
#Check if seg is an interface
#For fe0.
                irealn0 = e0.getNodeIndexIntoList(n0)
                realn0 = fe0.getCornerNode( irealn0 )
                irealn1 = e0.getNodeIndexIntoList(n1)
                realn1 = fe0.getCornerNode( irealn1 )
#For fe1.
                irealn0_2 = e1.getNodeIndexIntoList(n0)
                realn0_2 = fe1.getCornerNode( irealn0_2 )
                irealn1_2 = e1.getNodeIndexIntoList(n1)
                realn1_2 = fe1.getCornerNode( irealn1_2 )
                if realn0==realn0_2 and realn1==realn1_2:
#seg is not an interface
                    if fe0.material() != fe1.material():
# material(internal) boundary
#self.bdy_node_map[n0.meshindex] = 1
#self.bdy_node_map[n1.meshindex] = 1
                        self.bdy_node_map[realn0.index()] = 1
                        self.bdy_node_map[realn1.index()] = 1
                        if self.mesh.order() > 1:
#Interface branch
#for nd in self.getEdgeNodes((n0,n1)):
                            for nd in self.getRealEdgeNodes(fe0,
                                                            irealn0,realn0,
                                                            irealn1,realn1):
                                self.bdy_node_map[nd.index()] = 1
                else:
#seg is an interface; at least one realmesh node on seg
#does not match between the two realmesh elements
                    self.bdy_node_map[realn0.index()] = 1
                    self.bdy_node_map[realn1.index()] = 1
                    if self.mesh.order() > 1:
                        for nd in self.getRealEdgeNodes(fe0,
                                                        irealn0,realn0,
                                                        irealn1,realn1):
                            self.bdy_node_map[nd.index()] = 1
                    self.bdy_node_map[realn0_2.index()] = 1
                    self.bdy_node_map[realn1_2.index()] = 1
                    if self.mesh.order() > 1:
                        for nd in self.getRealEdgeNodes(fe1,
                                                        irealn0_2,realn0_2,
                                                        irealn1_2,realn1_2):
                            self.bdy_node_map[nd.index()] = 1
            elif self.contains(fe0):
                self.create_elem_bdynodemap(e0,fe0,n0,n1)
            elif self.contains(fe1):
                self.create_elem_bdynodemap(e1,fe1,n0,n1)

CSubProblem.create_bdy_node_map = _CSubProblem_create_bdy_node_map

# Create SC Patch.
def _CSubProblem_create_scpatch(self, skel):
## TODO: only call create_scpatch if necessary.  There used to be
## a _have_scpatches flag that was checked here and set to True if
## the patches were built, but it was neer set to False anywhere,
## and was a *class* variable, not an instance variable, so it's
## been removed.
#Interface branch
#Obtain the meshindex directly from realmesh nodes.
#When interfaces are present, a single skeleton node may correspond
#to more than one real mesh node.
#Old way:
#self.init_scpatches([node.meshindex for node in skel.node_iterator()])
#New way:
    indices = set()
    for skelel in skel.element_iterator():
        realel=self.mesh.getElement(skelel.meshindex)
        if self.contains(realel):
            for skelnode in skelel.nodes:
                realnode=realel.getCornerNode(
                    skelel.getNodeIndexIntoList(skelnode))
                indices.add(realnode.index())
    self.init_scpatches(list(indices))
    patches = scpatch.SCPatchCollection(self)  # helper object
# buildPatches calls CSubProblem::add_scpatch for each patch.
    patches.buildPatches(skel)

CSubProblem.create_scpatch = _CSubProblem_create_scpatch

# Superconvergent patch flux recovery.

# TODO: The recover_fluxes operation appears to be broken in the case
# where there are enclosed or partially-enclosed empty elements.  It
# works for the refinement tutorial, but not for simple test domains
# of the same shape, and seems to be independent of whether the
# elements are triangular or quad.  (SL: I could not reproduce this
# before the 2.1 release.)
def _CSubProblem_flux_recovery(self):
## TODO: only call recover_fluxes if the solution or mesh has
## changed.  There used to be a self._did_recover_fluxes flag that
## was checked here, and set to True after recovering the fluxes.
## However, it was never reset to False anywhere, and was a
## *class* variable, not an instance variable, so it's been
## deleted.
    self.init_nodalfluxes()
    self.recover_fluxes()           # C++ CSubProblem will do it.

CSubProblem.flux_recovery = _CSubProblem_flux_recovery



