From 8a2e7011e8a662234b3126074b5ef051fb85ce08 Mon Sep 17 00:00:00 2001
From: David Seus <david.seus@ians.uni-stuttgart.de>
Date: Wed, 27 Mar 2019 08:30:42 +0100
Subject: [PATCH] fix Dirichlett boundary implementation

---
 LDDsimulation.py   |  39 ++++----
 RR-2-patch-test.py | 110 +++++++++++++++-------
 domainPatch.py     | 225 ++++++++++++++++++++++++++++++++++++---------
 3 files changed, 277 insertions(+), 97 deletions(-)

diff --git a/LDDsimulation.py b/LDDsimulation.py
index a6848c8..bc3064f 100644
--- a/LDDsimulation.py
+++ b/LDDsimulation.py
@@ -95,6 +95,7 @@ class LDDsimulation(object):
                        # this is actually an array of bools
                        isRichards: bool,#
                        interface_def_points: tp.List[tp.List[df.Point]],#
+                       outer_boundary_def_points: tp.Dict[int, tp.Dict[int, tp.List[df.Point]]],#
                        adjacent_subdomains: tp.List[np.ndarray],#
                        mesh_resolution: float,#
                        viscosity: tp.Dict[int, tp.List[float]],#
@@ -103,7 +104,7 @@ class LDDsimulation(object):
                        lambda_param: tp.Dict[int, tp.Dict[str, float]],#
                        relative_permeability: tp.Dict[int, tp.Dict[str, tp.Callable[...,None]] ],#
                        saturation: tp.Dict[int, tp.Callable[...,None]],#
-                       timestep: tp.Dict[int, float],#
+                       timestep_size: tp.Dict[int, float],#
                        sources: tp.Dict[int, tp.Dict[str, str]],#
                        initial_conditions: tp.Dict[int, tp.Dict[str, str]],#
                        outer_dirichletBC: tp.Dict[int, tp.Dict[str, str]],#
@@ -115,6 +116,7 @@ class LDDsimulation(object):
         self.isRichards = isRichards
         self.mesh_resolution = mesh_resolution
         self.interface_def_points = interface_def_points
+        self.outer_boundary_def_points = outer_boundary_def_points
         self.adjacent_subdomains = adjacent_subdomains
         self.viscosity = viscosity
         self.porosity = porosity
@@ -122,7 +124,7 @@ class LDDsimulation(object):
         self.lambda_param = lambda_param
         self.relative_permeability = relative_permeability
         self.saturation = saturation
-        self.timestep = timestep
+        self.timestep_size = timestep_size
         self.sources = sources
         self.initial_conditions = initial_conditions
         self.outer_dirichletBC = outer_dirichletBC
@@ -273,18 +275,19 @@ class LDDsimulation(object):
             interface_list = self._get_interfaces(subdom_num)
             self.subdomain.update(#
                     {subdom_num : dp.DomainPatch(#
-                    subdomain_index = subdom_num,#
-                    isRichards = isR,#
-                    mesh = self.mesh_subdomain[subdom_num],#
-                    viscosity = self.viscosity[subdom_num],#
-                    porosity = self.porosity[subdom_num],#
-                    interfaces = self.interface,#
-                    has_interface = interface_list,#
-                    L = self.L[subdom_num],#
-                    lambda_param = self.lambda_param[subdom_num],#
-                    relative_permeability = self.relative_permeability[subdom_num],#
-                    saturation = self.saturation[subdom_num],#
-                    timestep = self.timestep[subdom_num],#
+                        subdomain_index = subdom_num,#
+                        isRichards = isR,#
+                        mesh = self.mesh_subdomain[subdom_num],#
+                        viscosity = self.viscosity[subdom_num],#
+                        porosity = self.porosity[subdom_num],#
+                        outer_boundary_def_points = self.outer_boundary_def_points[subdom_num],#
+                        interfaces = self.interface,#
+                        has_interface = interface_list,#
+                        L = self.L[subdom_num],#
+                        lambda_param = self.lambda_param[subdom_num],#
+                        relative_permeability = self.relative_permeability[subdom_num],#
+                        saturation = self.saturation[subdom_num],#
+                        timestep_size = self.timestep_size[subdom_num],#
                     )})
 
 
@@ -348,17 +351,17 @@ class LDDsimulation(object):
                 self.outerBC.update({num: dict()})
 
             pDC = self.outer_dirichletBC[num]
-            boundary_marker = subdomain.boundary_marker
+            boundary_marker = subdomain.outer_boundary_marker
             if subdomain.isRichards:
                 # note that the default interpolation degree is 2
                 pDCw = df.Expression(pDC['wetting'], domain = mesh, degree = interpolation_degree, t=time)
-                self.outerBC[num].update({'wetting': df.DirichletBC(V['wetting'], pDCw, boundary_marker, 0)})
+                self.outerBC[num].update({'wetting': df.DirichletBC(V['wetting'], pDCw, boundary_marker, 1)})
             else:
                 pDCw = df.Expression(pDC['wetting'], domain = mesh, degree = interpolation_degree, t=time)
                 pDCnw = df.Expression(pDC['nonwetting'], domain = mesh, degree = interpolation_degree, t=time)
                 self.outerBC[num].update(#
-                    {'wetting': df.DirichletBC(V['wetting'], pDCw, boundary_marker, 0)},#
-                    {'nonwetting': df.DirichletBC(V['nonwetting'], pDCnw, boundary_marker, 0)},#
+                    {'wetting': df.DirichletBC(V['wetting'], pDCw, boundary_marker, 1)},#
+                    {'nonwetting': df.DirichletBC(V['nonwetting'], pDCnw, boundary_marker, 1)},#
                 )
 
     def _eval_sources(self, interpolation_degree: int = 2, time: float = 0):
diff --git a/RR-2-patch-test.py b/RR-2-patch-test.py
index a765fad..8fbb509 100755
--- a/RR-2-patch-test.py
+++ b/RR-2-patch-test.py
@@ -23,11 +23,38 @@ sub_domain1_vertices = [interface12_vertices[0],
                         interface12_vertices[1],
                         df.Point(1.0,1.0),
                         df.Point(0.0,1.0) ]
+
+# vertex coordinates of the outer boundaries. If it can not be specified as a
+# polygon, use an entry per boundary polygon. This information is used for defining
+# the Dirichlet boundary conditions. If a domain is completely internal, the
+# dictionary entry should be 0: None
+subdomain1_outer_boundary_verts = {
+    0: [interface12_vertices[0], #
+        df.Point(0.0,1.0), #
+        df.Point(1.0,1.0), #
+        interface12_vertices[1]]
+}
 # subdomain2
 sub_domain2_vertices = [df.Point(0.0,0.0),
                         df.Point(1.0,0.0),
                         interface12_vertices[1],
                         interface12_vertices[0] ]
+
+subdomain2_outer_boundary_verts = {
+    0: [interface12_vertices[1], #
+        df.Point(1.0,0.0), #
+        df.Point(0.0,0.0), #
+        interface12_vertices[0]]
+}
+# subdomain2_outer_boundary_verts = {
+#     0: [interface12_vertices[0], df.Point(0.0,0.0)],#
+#     1: [df.Point(0.0,0.0), df.Point(1.0,0.0)], #
+#     2: [df.Point(1.0,0.0), interface12_vertices[1]]
+# }
+# subdomain2_outer_boundary_verts = {
+#     0: None
+# }
+
 # list of subdomains given by the boundary polygon vertices.
 # Subdomains are given as a list of dolfin points forming
 # a closed polygon, such that mshr.Polygon(subdomain_def_points[i]) can be used
@@ -39,6 +66,13 @@ subdomain_def_points = [sub_domain0_vertices,#
                       sub_domain2_vertices]
 # in the below list, index 0 corresponds to the 12 interface which has index 1
 interface_def_points = [interface12_vertices]
+
+outer_boundary_def_points = {
+    # subdomain number
+    1 : subdomain1_outer_boundary_verts,
+    2 : subdomain2_outer_boundary_verts
+}
+
 # adjacent_subdomains[i] contains the indices of the subdomains sharing the
 # interface i (i.e. given by interface_def_points[i]).
 adjacent_subdomains = [[1,2]]
@@ -49,7 +83,7 @@ dt1 = 0.1
 dt2 = dt1
 # the timestep is taken as a dictionary to be able to run different timesteps on
 # different subdomains.
-timestep = {
+timestep_size = {
     1: dt1,
     2: dt2
 }
@@ -145,6 +179,7 @@ simulation.set_parameters(output_dir = "",#
     subdomain_def_points = subdomain_def_points,#
     isRichards = isRichards,#
     interface_def_points = interface_def_points,#
+    outer_boundary_def_points = outer_boundary_def_points,#
     adjacent_subdomains = adjacent_subdomains,#
     mesh_resolution = 2,#
     viscosity = viscosity,#
@@ -153,7 +188,7 @@ simulation.set_parameters(output_dir = "",#
     lambda_param = lambda_param,#
     relative_permeability = relative_permeability,#
     saturation = sat_pressure_relationship,#
-    timestep = timestep,#
+    timestep_size = timestep_size,#
     sources = source_expression,#
     initial_conditions = initial_condition,#
     outer_dirichletBC = dirichletBC,#
@@ -179,32 +214,32 @@ mesh_subdomain = simulation.mesh_subdomain
 interface = simulation.interface
 interface_marker = simulation.interface_marker
 
-boundary_marker1 = simulation.subdomain[1].boundary_marker
-boundary_marker2 = simulation.subdomain[2].boundary_marker
+boundary_marker1 = simulation.subdomain[1].outer_boundary_marker
+boundary_marker2 = simulation.subdomain[2].outer_boundary_marker
 dx_1 = simulation.subdomain[1].dx
 dx_2 = simulation.subdomain[2].dx
 ds_1 = simulation.subdomain[1].ds
 ds_2 = simulation.subdomain[2].ds
 
-subdomain_boundary_marker1 = df.MeshFunction('size_t', mesh_subdomain[1], mesh_subdomain[1].topology().dim()-1)
-subdomain_boundary_marker2 = df.MeshFunction('size_t', mesh_subdomain[2], mesh_subdomain[2].topology().dim()-1)
-subdomain_boundary_marker1.set_all(0)
-subdomain_boundary_marker2.set_all(0)
-# print(dir(subdomain_boundary_marker1))
+interface_marker1 = simulation.subdomain[1].interface_marker#df.MeshFunction('size_t', mesh_subdomain[1], mesh_subdomain[1].topology().dim()-1)
+interface_marker2 = simulation.subdomain[2].interface_marker#df.MeshFunction('size_t', mesh_subdomain[2], mesh_subdomain[2].topology().dim()-1)
+# interface_marker1.set_all(0)
+# interface_marker2.set_all(0)
+# print(dir(interface_marker1))
 
-interface[0].mark(subdomain_boundary_marker1, 1)
-interface[0].mark(subdomain_boundary_marker2, 1)
-# subdomain_boundary_marker1 = simulation.subdomain[1].boundary_marker
+interface[0].mark(interface_marker1, 1)
+interface[0].mark(interface_marker2, 1)
+# interface_marker1 = simulation.subdomain[1].boundary_marker
 
 # domain measures
 
 # dx1 = simulation.subdomain[1].dx
-dx1 = df.Measure('dx', domain = mesh_subdomain[1], subdomain_data = subdomain_boundary_marker1)
-dx2 = df.Measure('dx', domain = mesh_subdomain[2], subdomain_data = subdomain_boundary_marker2)
+dx1 = df.Measure('dx', domain = mesh_subdomain[1])
+dx2 = df.Measure('dx', domain = mesh_subdomain[2])
 
 # boundary measures
-ds1 = df.Measure('ds', domain = mesh_subdomain[1], subdomain_data = subdomain_boundary_marker1)
-ds2 = df.Measure('ds', domain = mesh_subdomain[2], subdomain_data = subdomain_boundary_marker2)
+ds1 = df.Measure('ds', domain = mesh_subdomain[1], subdomain_data = interface_marker1)
+ds2 = df.Measure('ds', domain = mesh_subdomain[2], subdomain_data = interface_marker2)
 
 V1 = df.FunctionSpace(mesh_subdomain[1], 'P', 1)
 p1_exact = df.Expression('1.0 - (1.0 + t*t)*(1.0 + x[0]*x[0] + x[1]*x[1])', domain = mesh_subdomain[1], degree = 2, t = 0)
@@ -229,18 +264,18 @@ dom2_parent_vertex_indices = submesh2_data.array('parent_vertex_indices',0)
 # interface_coordinates = interface[0].coordinates(interface_marker, 1, print_coordinates = True)
 # interface_coordinates = interface[0]._vertex_indices(interface_marker, 1, print_vertex_indices = True)
 print("\non subdomain2: \n")
-# dom2_interface_coordinates = interface[0].coordinates(subdomain_boundary_marker2, 1, print_coordinates = True)
-dom2_interface_def_points = interface[0]._vertex_indices(subdomain_boundary_marker2, 1, print_vertex_indices = True)
+# dom2_interface_coordinates = interface[0].coordinates(interface_marker2, 1, print_coordinates = True)
+dom2_interface_def_points = interface[0]._vertex_indices(interface_marker2, 1, print_vertex_indices = True)
 
 
-dofs_on_interface1 = interface[0].dofs_on_interface(V1, subdomain_boundary_marker1, 1)
+dofs_on_interface1 = interface[0].dofs_on_interface(V1, interface_marker1, 1)
 print("Dofs on Interface dom1", dofs_on_interface1)
 
 V2 = df.FunctionSpace(mesh_subdomain[2], 'P', 1)
-dofs_on_interface2 = interface[0].dofs_on_interface(V2, subdomain_boundary_marker2, 1)
+dofs_on_interface2 = interface[0].dofs_on_interface(V2, interface_marker2, 1)
 print("Dofs on Interface dom1", dofs_on_interface2)
 
-# markermesh = subdomain_boundary_marker2.mesh()
+# markermesh = interface_marker2.mesh()
 # functionSpaceMesh = V2.mesh()
 # print("Domain 2 mesh extracted from markerfunction:\n", markermesh.coordinates())
 # print("Domain 2 mesh extracted from Function Space V2:\n", functionSpaceMesh.coordinates())
@@ -295,8 +330,8 @@ for x in coordinates1:
      i += 1
 print("\non subdomain1: \n")
 print("vertices of subdomain1: \n", coordinates1)
-# dom1_interface_coordinates = interface[0].coordinates(subdomain_boundary_marker1, 1, print_coordinates = True)
-dom1_interface_def_points = interface[0]._vertex_indices(subdomain_boundary_marker1, 1, print_vertex_indices = True)
+# dom1_interface_coordinates = interface[0].coordinates(interface_marker1, 1, print_coordinates = True)
+dom1_interface_def_points = interface[0]._vertex_indices(interface_marker1, 1, print_vertex_indices = True)
 
 interface[0].write_dofs(from_function = f1test, #
                         interface_dofs = dofs_on_interface1,#
@@ -319,8 +354,8 @@ for x in coordinates2:
 print("\non subdomain2: \n")
 print("vertices of subdomain2: \n", coordinates2)
 
-# dom2_interface_coordinates = interface[0].coordinates(subdomain_boundary_marker2, 1, print_coordinates = True)
-dom2_interface_def_points = interface[0]._vertex_indices(subdomain_boundary_marker2, 1, print_vertex_indices = True)
+# dom2_interface_coordinates = interface[0].coordinates(interface_marker2, 1, print_coordinates = True)
+dom2_interface_def_points = interface[0]._vertex_indices(interface_marker2, 1, print_vertex_indices = True)
 print("Parent indices", dom2_parent_vertex_indices)
 
 
@@ -359,11 +394,11 @@ p1_0 = df.interpolate(p1_initial, V1)
 p2_0 = df.interpolate(p2_initial, V2)
 
 # Initial boundary value for the pressure on interface
-p_gamma1 = df.DirichletBC(V1, df.Constant(0.5), subdomain_boundary_marker1, 1)
-p_gamma2 = df.DirichletBC(V2, df.Constant(0.1), subdomain_boundary_marker2, 1)
+p_gamma1 = df.DirichletBC(V1, df.Constant(0.5), interface_marker1, 1)
+p_gamma2 = df.DirichletBC(V2, df.Constant(0.1), interface_marker2, 1)
 
-outerBC1 = df.DirichletBC(V1, p1_exact, subdomain_boundary_marker1, 0)
-outerBC2 = df.DirichletBC(V2, p2_exact, subdomain_boundary_marker2, 0)
+outerBC1 = df.DirichletBC(V1, p1_exact, boundary_marker1, 1)
+outerBC2 = df.DirichletBC(V2, p2_exact, boundary_marker2, 1)
 
 
 ### source terms
@@ -388,16 +423,22 @@ g1_i = -lambda1*p1_i
 g2_i = -lambda1*p2_i
 
 for iterations in range(1):
-    a1 = (L1*u1*v1)*dx1 + (timestep*df.dot(relative_permeability(saturation(p1_i, 1), 1)*df.grad(u1), df.grad(v1)))*dx1 + (timestep*lambda1*u1*v1)*ds1
+    #   + relative_permeability(saturation(p1_i, 1), 1)* +
+    a1 = (df.inner(df.grad(u1), df.grad(v1)))*dx_1 + (timestep*lambda1*u1*v1)*ds1(1) #timestep*
     rhs1 = (L1*p1_i*v1)*dx1 - ((saturation(p1_i, 1) - saturation(p1_0, 1))*v1)*dx1 + (timestep*(source1 - g1_i)*v1)*ds1
     A1 = df.assemble(a1)
+    dsform = (timestep*lambda1*u1*v1)*ds1(1)
+    dsform_vec = df.assemble(dsform)
     b1 = df.assemble(rhs1)
     p_gamma1.apply(A1,b1)
     outerBC1.apply(A1,b1)
     u  = df.Function(V1)
     U1 = u.vector()
     df.solve(A1,U1,b1)
-    print('solution:\n', U1)
+    print("Form:\n", A1.array())
+    print("RHS:\n", b1.get_local())
+    print("ds term:\n", dsform_vec.array())
+    print('solution:\n', U1.get_local())
     p1_i.vector()[:] = U1
     interface[0].write_dofs(from_function = p1_i, #
                             interface_dofs = dofs_on_interface1,#
@@ -408,10 +449,13 @@ for iterations in range(1):
 #
 # Save mesh to file
 df.File('./test_domain_layered_soil.xml.gz') << mesh_subdomain[0]
+df.File('./test_domain_mesh.pvd') << mesh_subdomain[0]
 df.File('./test_global_interface_marker.pvd') << interface_marker
 df.File('./test_subdomain1.xml.gz') << mesh_subdomain[1]
 df.File('./test_subdomain2.xml.gz') << mesh_subdomain[2]
 df.File('./test_domain_marker.pvd') << domain_marker
 df.File('./test_domain_layered_soil_solution.pvd') << u
-df.File('./test_subdomain1_boundary_marker.pvd') << subdomain_boundary_marker1
-df.File('./test_subdomain2_boundary_marker.pvd') << subdomain_boundary_marker2
+df.File('./test_subdomain1_interface_marker.pvd') << interface_marker1
+df.File('./test_subdomain2_interface_marker.pvd') << interface_marker2
+df.File('./test_subdomain1_boundary_marker.pvd') << boundary_marker1
+df.File('./test_subdomain2_boundary_marker.pvd') << boundary_marker2
diff --git a/domainPatch.py b/domainPatch.py
index 5e298c6..3855a6d 100644
--- a/domainPatch.py
+++ b/domainPatch.py
@@ -108,6 +108,7 @@ class DomainPatch(df.SubDomain):
                  mesh: df.Mesh,#
                  porosity: float,#
                  viscosity: tp.Dict[str, float],#
+                 outer_boundary_def_points: tp.Dict[str, tp.List[df.Point]],#
                  # tp.List[Interface] doesn't work, because it hasen't been defined yet.
                  interfaces: tp.List[object],#
                  has_interface: tp.List[int],#
@@ -115,7 +116,7 @@ class DomainPatch(df.SubDomain):
                  lambda_param: tp.Dict[str, float],#
                  relative_permeability: tp.Dict[str, tp.Callable[...,None]],#
                  saturation: tp.Callable[..., None],#
-                 timestep: float,#
+                 timestep_size: float,#
                  ):
         # because we declare our own __init__ method for the derived class BoundaryPart,
         # we overwrite the __init__-method of the parent class df.SubDomain. However,
@@ -128,6 +129,7 @@ class DomainPatch(df.SubDomain):
         self.mesh = mesh
         self.porosity = porosity
         self.viscosity = viscosity
+        self.outer_boundary_def_points = outer_boundary_def_points
         self.interface = interfaces
         self.has_interface = has_interface
         self.L = L
@@ -135,11 +137,12 @@ class DomainPatch(df.SubDomain):
         self.relative_permeability = relative_permeability
         self.saturation = saturation
         # timestep size, tau in the paper
-        self.timestep = timestep
+        self.timestep_size = timestep_size
         ### Class variables set by methods
         # dictionary holding the dof indices corresponding to an interface of
         # given interface. see self._calc_dof_indices_of_interfaces()
         self._dof_indices_of_interface = dict()
+        self.outer_boundary = []
         # measures are set by self._init_measures()
         self.iteration_number = 0
         self.dx = None
@@ -153,9 +156,12 @@ class DomainPatch(df.SubDomain):
         self.pressure_prev_timestep = None
         # test function(s) for the form set by _init_function_space
         self.TestFunction = None
+        # # dictionary which is initiated by _init_function_space holding the
+        # # the gli term needed for the form assemply.
+        # self.gli = None
         self._init_function_space()
         self._init_dof_and_vertex_maps()
-        self._init_boundary_markers()
+        self._init_markers()
         self._init_measures()
         self._calc_dof_indices_of_interfaces()
 
@@ -188,7 +194,7 @@ class DomainPatch(df.SubDomain):
         # define measures
         dx = self.dx
         ds = self.ds
-        dt = self.timestep
+        dt = self.timestep_size
         if self.isRichards:
             Lw = self.L['wetting']
             # this is pw_i in the paper
@@ -201,16 +207,19 @@ class DomainPatch(df.SubDomain):
             kw = self.relative_permeability['wetting']
             S = self.saturation
             mu_w = self.viscosity['wetting']
+            source_w = self.source['wetting']
             # we need to have all interfaces in the form
             interface_forms = []
             for index in self.has_interface:
                 interface_forms.append((dt*Lambda*pw*v)*ds[index])
             form = (Lw*pw*v)*dx + (dt/mu_w*df.dot(kw(S(pw_prev_iter))*df.grad(pw), df.grad(v)))*dx + df.sum(interface_forms)
             # assemble rhs for
-            gi = self._calc_gi(iteration = iter_num)
             rhs_interface_forms = []
+            self._calc_gli_term(iteration = iter_num)
+            # gli = self.gli['wetting']
             for index in self.has_interface:
-                rhs_interface_forms.append((dt*gi*v)*ds[index])
+                gli = self.interface[index].gli_term[self.subdomain_index]['wetting']
+                rhs_interface_forms.append((dt*gli*v)*ds[index])
             rhs = (Lw*pw_prev_iter*v)*dx - ((S(pw_prev_iter) - S(pw_prev_timestep)*v)*dx + dt*source_w*v)*dx - df.sum(rhs_interface_forms)
             form_and_rhs = {#
                 'form': form,#
@@ -240,6 +249,7 @@ class DomainPatch(df.SubDomain):
             self.function_space = {'wetting': df.FunctionSpace(self.mesh, 'P', 1)}
             self.pressure = df.TrialFunction(self.function_space['wetting'])
             self.testfunction = df.TestFunction(self.function_space['wetting'])
+            # self.gli = {'wetting' : df.interpolate(df.Constant(0), self.function_space['wetting'])}
         else:
             self.function_space = {#
                 'wetting'    : df.FunctionSpace(self.mesh, 'P', 1),#
@@ -253,6 +263,10 @@ class DomainPatch(df.SubDomain):
                 'wetting'    : df.TestFunction(self.function_space['wetting']),
                 'nonwetting' : df.TestFunction(self.function_space['nonwetting'])
                 }
+            # self.gli = {#
+            #     'wetting' : df.interpolate(df.Constant(0), self.function_space['wetting']),#
+            #     'nonwetting' : df.interpolate(df.Constant(0), self.function_space['nonwetting'])
+            # }
 
     def _init_dof_and_vertex_maps(self) -> None:
         """ calculate dof maps and the vertex to parent map.
@@ -282,18 +296,36 @@ class DomainPatch(df.SubDomain):
                 'nonwetting':df.vertex_to_dof_map(self.function_space['nonwetting'])#
                 }
 
-    def _init_boundary_markers(self) -> None:
+    def _init_markers(self) -> None:
         """ define boundary markers
 
         This method sets the class Variables
-            self.boundary_marker
+            self.interface_marker
+            self.outer_boundary_marker
 
         """
-        self.boundary_marker = df.MeshFunction('size_t', self.mesh, self.mesh.topology().dim()-1)
-        self.boundary_marker.set_all(0)
+        self.outer_boundary_marker = df.MeshFunction('size_t', self.mesh, self.mesh.topology().dim()-1)
+        self.outer_boundary_marker.set_all(0)
+        self.interface_marker = df.MeshFunction('size_t', self.mesh, self.mesh.topology().dim()-1)
+        self.interface_marker.set_all(0)
         for glob_index in self.has_interface:
             # each interface gets marked with the global interface index.
-            self.interface[glob_index].mark(self.boundary_marker, glob_index)
+            self.interface[glob_index].mark(self.interface_marker, glob_index)
+        # create outerBoundary objects and mark outer boundaries with 1
+        for index, boundary_points in self.outer_boundary_def_points.items():
+            # if our domain patch has no outer boundary, this should be reflected
+            # by the value None in the dictionary
+            if boundary_points is None:
+                #
+                self.outer_boundary = None
+            else:
+                self.outer_boundary.append(#
+                    BoundaryPart(vertices = boundary_points,#
+                        internal = False,
+                        tol=self.mesh.hmin()/100 )
+                )
+                self.outer_boundary[index].mark(self.outer_boundary_marker, 1)
+
 
     def _init_measures(self):
         """ define measures for the governing form
@@ -305,12 +337,12 @@ class DomainPatch(df.SubDomain):
         # domain measure
         self.dx = df.Measure('dx', domain = self.mesh)
         # measure over the interfaces
-        self.ds = df.Measure('ds', domain = self.mesh, subdomain_data = self.boundary_marker)
+        self.ds = df.Measure('ds', domain = self.mesh, subdomain_data = self.interface_marker)
 
     def _calc_dof_indices_of_interfaces(self):
         """ calculate dof indices of each interface """
         V = self.function_space
-        marker = self.boundary_marker
+        marker = self.interface_marker
         for ind in self.has_interface:
             self._dof_indices_of_interface.update(#
                 {ind: dict()}
@@ -325,8 +357,103 @@ class DomainPatch(df.SubDomain):
                      'nonwetting': self.interface[ind].dofs_on_interface(V['nonwetting'], marker, ind)}
                 )
 
-
-    # END is_Richards
+    # def _calc_gli_term(self, iteration: int) -> None: # tp.Dict[str, fl.Form]
+    #     """calculate the gl terms for the iteration number iteration
+    #
+    #     calculate the gl terms for the iteration number iteration and save them
+    #     to self.gli.
+    #     """
+    #     for ind in self.has_interface:
+    #         # self._calc_gli_term_on(interface, iteration)
+    #         interface = self.interface[ind]
+    #         subdomain = self.subdomain_index
+    #         # neighbour index
+    #         neighbour = interface.neighbour[subdomain]
+    #         neighbour_iter_num = interface.current_iteration[neighbour]
+    #
+    #         Lambda = self.lambda_param
+    #         if iteration == 0:
+    #             n = df.FacetNormal(self.mesh)
+    #             pw_prev_iter = self.pressure_prev_timestep
+    #             k = self.relative_permeability
+    #             S = self.saturation
+    #             mu = self.viscosity
+    #
+    #             if not neighbour_iter_num == 0:
+    #                 # save the gl term from the neighbour first to use it in the
+    #                 # next iteration
+    #                 if self.isRichards:
+    #                     interface.gli_term_prev[subdomain].update(#
+    #                         {'wetting': interface.gli_term[neighbour]['wetting']}
+    #                         )
+    #                 else:
+    #                     interface.gli_term_prev[subdomain].update(#
+    #                         {'wetting': interface.gli_term[neighbour]['wetting']},#
+    #                         {'nonwetting': interface.gli_term[neighbour]['nonwetting']}
+    #                         )
+    #
+    #             if self.isRichards:
+    #                 mu_w = mu['wetting']
+    #                 kw = k['wetting']
+    #                 pwn_prev = pw_prev_iter['wetting']
+    #                 #calc gl0 from the flux term
+    #                 flux = -1/mu_w*df.dot(kw(S(pwn_prev))*df.grad(pwn_prev)
+    #                 gl0 = (df.dot(flux, n) - Lambda['wetting']*pwn_prev)
+    #                 interface.gli_term[subdomain].update({'wetting': gl0})
+    #             else:
+    #                 mu_w = mu['wetting']
+    #                 mu_nw = mu['nonwetting']
+    #                 pwn_prev = pw_prev_iter['wetting']
+    #                 pnwn_prev = pw_prev_iter['nonwetting']
+    #                 kw = k['wetting']
+    #                 knw = k['nonwetting']
+    #                 #calc gl0 from the flux term
+    #                 fluxw = -1/mu_w*df.dot(kw(S(pnwn_prev - pwn_prev))*df.grad(pwn_prev)
+    #                 fluxnw = -1/mu_nw*df.dot(knw(1-S(pnwn_prev - pwn_prev))*df.grad(pnwn_prev)
+    #                 gwl0 = (df.dot(fluxw, n) - Lambda['wetting']*pwn_prev)
+    #                 gnwl0 = (df.dot(fluxnw, n) - Lambda['nonwetting']*pnwn_prev)
+    #                 interface.gli_term[subdomain].update({'wetting': gwl0})
+    #                 interface.gli_term[subdomain].update({'nonwetting': gnwl0})
+    #
+    #             interface.current_iteration[subdomain] += 1
+    #
+    #         else:
+    #             if neighbour_iter_num == iteration:
+    #                 if self.isRichards:
+    #                     interface.gli_term_prev[subdomain].update(#
+    #                         {'wetting': interface.gli_term[neighbour]['wetting']}
+    #                         )
+    #
+    #                     pwn_prev = pw_prev_iter['wetting']
+    #                     #calc gl0 from the flux term
+    #                     flux = -1/mu_w*df.dot(kw(S(pwn_prev))*df.grad(pwn_prev)
+    #                     gl0 = (df.dot(flux, n) - Lambda['wetting']*pwn_prev)
+    #                     interface.gli_term[subdomain].update({'wetting': gl0})
+    #                 else:
+    #                     mu_w = mu['wetting']
+    #                     mu_nw = mu['nonwetting']
+    #                     pwn_prev = pw_prev_iter['wetting']
+    #                     pnwn_prev = pw_prev_iter['nonwetting']
+    #                     kw = k['wetting']
+    #                     knw = k['nonwetting']
+    #                     #calc gl0 from the flux term
+    #                     fluxw = -1/mu_w*df.dot(kw(S(pnwn_prev - pwn_prev))*df.grad(pwn_prev)
+    #                     fluxnw = -1/mu_nw*df.dot(knw(1-S(pnwn_prev - pwn_prev))*df.grad(pnwn_prev)
+    #                     gwl0 = (df.dot(fluxw, n) - Lambda['wetting']*pwn_prev)
+    #                     gnwl0 = (df.dot(fluxnw, n) - Lambda['nonwetting']*pnwn_prev)
+    #                     interface.gli_term[subdomain].update({'wetting': gwl0})
+    #                     interface.gli_term[subdomain].update({'nonwetting': gnwl0})
+    #
+    #             else:
+    #                 print('')
+    #             interface.current_iteration[subdomain] += 1
+    # # def _calc_gli_term_on(self, interface: int, iteration: int) -> None:
+    # #     """calculate the gl terms for the iteration number iteration
+    # #
+    # #     calculate the gl terms for the iteration number iteration and save them
+    # #     to self.gli.
+    # #     """
+    # # END is_Richards
 
 # END OF CLASS DomainPatch
 
@@ -550,14 +677,21 @@ class Interface(BoundaryPart):
         # method init_interface_values().
         self.pressure_values = None
         self.pressure_values_prev = None
-        # same as for self.pressure but for the interface decoupling terms gl.
-        self.gl_values = None
-        self.gl_values_prev = None
+        # dictionaries holding the forms for the decoupling terms
+        self.gli_term = None
+        self.gli_term_prev = None
         # dictionary holding the current iteration number i for each of the adjacent
         # subdomains with index in adjacent_subdomains
-        self.current_iteration = dict()
-        self.current_iteration.update({self.adjacent_subdomains[0]: 0})
-        self.current_iteration.update({self.adjacent_subdomains[1]: 0})
+        self.current_iteration = {#
+            self.adjacent_subdomains[0]: 0,#
+            self.adjacent_subdomains[1]: 0
+        }
+        # dictionary holding the neighbouring domain index relative to the key index
+        # this is used for calculating the gl_terms.
+        self.neighbour = {
+            self.adjacent_subdomains[0]: self.adjacent_subdomains[1],#
+            self.adjacent_subdomains[1]: self.adjacent_subdomains[0]#
+        }
 
 
         # make the parent class methods available.
@@ -567,9 +701,6 @@ class Interface(BoundaryPart):
     # def set_domain_index(self, domain_index: int):
     #     self.domain_index = domain_index
 
-    def set_adjacent_subdomains(self, adjacent_subdomains: np.array):
-        self.adjacent_subdomains = adjacent_subdomains
-
     def dofs_on_interface(self,#
                         function_space: df.FunctionSpace,#
                         interface_marker: df.MeshFunction, #
@@ -623,25 +754,27 @@ class Interface(BoundaryPart):
         self.pressure_values_prev.update({self.adjacent_subdomains[0]: dict()})
         self.pressure_values_prev.update({self.adjacent_subdomains[1]: dict()})
 
-        self.gl_values = dict()
-        self.gl_values.update({self.adjacent_subdomains[0]: dict()})
-        self.gl_values.update({self.adjacent_subdomains[1]: dict()})
-        self.gl_values_prev = dict()
-        self.gl_values_prev.update({self.adjacent_subdomains[0]: dict()})
-        self.gl_values_prev.update({self.adjacent_subdomains[1]: dict()})
+        self.gli_term = dict()
+        self.gli_term.update({self.adjacent_subdomains[0]: dict()})
+        self.gli_term.update({self.adjacent_subdomains[1]: dict()})
+        self.gli_term_prev = dict()
+        self.gli_term_prev.update({self.adjacent_subdomains[0]: dict()})
+        self.gli_term_prev.update({self.adjacent_subdomains[1]: dict()})
         # we initialise all dictionaries to accomodate two phases, to be more
         # flexible with coupling Richards and Two-phase models.
         for subdom_ind in self.adjacent_subdomains:
             for phase in ['wetting', 'nonwetting']:
                 self.pressure_values[subdom_ind].update({phase: dict()})
                 self.pressure_values_prev[subdom_ind].update({phase: dict()})
-                self.gl_values[subdom_ind].update({phase: dict()})
-                self.gl_values_prev[subdom_ind].update({phase: dict()})
+                # the gli_term will hold forms not functions, therefor we leave
+                # the dictionary empty. it will be populated by domainPatch._calc_gli_term()
+                # self.gli_term[subdom_ind].update({phase: dict()})
+                # self.gli_term_prev[subdom_ind].update({phase: dict()})
                 for vert_ind in vertex_indices:
                     self.pressure_values[subdom_ind][phase].update({vert_ind: 0.0})
                     self.pressure_values_prev[subdom_ind][phase].update({vert_ind: 0.0})
-                    self.gl_values[subdom_ind][phase].update({vert_ind: 0.0})
-                    self.gl_values_prev[subdom_ind][phase].update({vert_ind: 0.0})
+                    # self.gli_term[subdom_ind][phase].update({vert_ind: 0.0})
+                    # self.gli_term_prev[subdom_ind][phase].update({vert_ind: 0.0})
         # end init_interface_values
 
     def write_dofs(self, from_function: df.Function, #
@@ -661,8 +794,8 @@ class Interface(BoundaryPart):
         of the interface
             self.pressure_values[subdomain_ind][phase], (if pressure == True)
             self.pressure_values_prev[subdomain_ind][phase], (if previous_iter == True)
-            self.gl_values[subdomain_ind][phase], (if gl == True)
-            self.gl_values_prev[subdomain_ind][phase] (if previous_iter == True)
+            self.gli_term[subdomain_ind][phase], (if gl == True)
+            self.gli_term_prev[subdomain_ind][phase] (if previous_iter == True)
 
         # Parameters
         from_function:              #type: df.Function, function to save the dofs to
@@ -706,14 +839,14 @@ class Interface(BoundaryPart):
                 for dof_index in interface_dofs:
                     # from_function.vector() is orderd by dof and not by vertex numbering of the mesh.
                     # parent needs a mesh vertex index of the submesh, therefore d2v is needed.
-                    self.gl_values[subdomain_ind][phase].update({parent[d2v[dof_index]]: from_function.vector()[dof_index]})
-                print("have written gl interface values\n", self.gl_values[subdomain_ind][phase])
+                    self.gli_term[subdomain_ind][phase].update({parent[d2v[dof_index]]: from_function.vector()[dof_index]})
+                print("have written gl interface values\n", self.gli_term[subdomain_ind][phase])
             else:
                 for dof_index in interface_dofs:
                     # from_function.vector() is orderd by dof and not by vertex numbering of the mesh.
                     # parent needs a mesh vertex index of the submesh, therefore d2v is needed.
-                    self.gl_values_prev[subdomain_ind][phase].update({parent[d2v[dof_index]]: from_function.vector()[dof_index]})
-                print("have written previous gl interface values\n", self.gl_values_prev[subdomain_ind][phase])
+                    self.gli_term_prev[subdomain_ind][phase].update({parent[d2v[dof_index]]: from_function.vector()[dof_index]})
+                print("have written previous gl interface values\n", self.gli_term_prev[subdomain_ind][phase])
 
     def read_dofs(self, to_function: df.Function, #
                   interface_dofs: np.array,#
@@ -731,8 +864,8 @@ class Interface(BoundaryPart):
         Read the degrees of freedom of one of the dictionaries of the interface
             self.pressure_values[subdomain_ind][phase], (if pressure == True)
             self.pressure_values_prev[subdomain_ind][phase], (if previous_iter == True)
-            self.gl_values[subdomain_ind][phase], (if gl == True)
-            self.gl_values_prev[subdomain_ind][phase] (if previous_iter == True)
+            self.gli_term[subdomain_ind][phase], (if gl == True)
+            self.gli_term_prev[subdomain_ind][phase] (if previous_iter == True)
         and overwrite the dofs of the function to_function with indices
         interface_dofs, i.e. update the interface dofs of the function.
         .
@@ -782,15 +915,15 @@ class Interface(BoundaryPart):
                 for dof_index in interface_dofs:
                     # from_function.vector() is orderd by dof and not by vertex numbering of the mesh.
                     # parent needs a mesh vertex index of the submesh, therefore d2v is needed.
-                    to_function.vector()[dof_index] = self.gl_values[subdomain_ind][phase][parent[d2v[dof_index]]]
-                print("read gl interface values\n", self.gl_values[subdomain_ind][phase])
+                    to_function.vector()[dof_index] = self.gli_term[subdomain_ind][phase][parent[d2v[dof_index]]]
+                print("read gl interface values\n", self.gli_term[subdomain_ind][phase])
                 print("wrote these dofs to function \n", to_function.vector()[:])
             else:
                 for dof_index in interface_dofs:
                     # from_function.vector() is orderd by dof and not by vertex numbering of the mesh.
                     # parent needs a mesh vertex index of the submesh, therefore d2v is needed.
-                    to_function.vector()[dof_index] = self.gl_values_prev[subdomain_ind][phase][parent[d2v[dof_index]]]
-                print("read gl interface values\n", self.gl_values_prev[subdomain_ind][phase])
+                    to_function.vector()[dof_index] = self.gli_term_prev[subdomain_ind][phase][parent[d2v[dof_index]]]
+                print("read gl interface values\n", self.gli_term_prev[subdomain_ind][phase])
                 print("wrote these dofs to function \n", to_function.vector()[:])
 
     #### Private Methods ####
-- 
GitLab