From e9dadcd1fdb71eb4f28eef45f8a44fa0b2e52e52 Mon Sep 17 00:00:00 2001 From: David Seus <david.seus@ians.uni-stuttgart.de> Date: Fri, 14 Jun 2019 16:06:02 +0200 Subject: [PATCH] rewrite init_interface_values --- LDDsimulation/LDDsimulation.py | 17 +- LDDsimulation/boundary_and_interface.py | 273 ++++++++++++++++---- LDDsimulation/domainPatch.py | 327 +++++++++++++++++++++--- RR-2-patch-test-case/RR-2-patch-test.py | 2 +- 4 files changed, 526 insertions(+), 93 deletions(-) diff --git a/LDDsimulation/LDDsimulation.py b/LDDsimulation/LDDsimulation.py index 84c441f..dc8eb64 100644 --- a/LDDsimulation/LDDsimulation.py +++ b/LDDsimulation/LDDsimulation.py @@ -417,7 +417,7 @@ class LDDsimulation(object): if iteration > 1: subsequent_iter_error = dict() for phase in subdomain.has_phases: - error = df.Function(subdomain.function_space[phase]) + error = df.Function(subdomain.function_space["pressure"][phase]) error.assign(subdomain.pressure[phase] - subdomain.pressure_prev_iter[phase]) error_calculated = df.norm(error, 'L2') subsequent_iter_error.update( @@ -678,7 +678,7 @@ class LDDsimulation(object): for phase in subdomain.has_phases: pa_exact = subdomain.pressure_exact[phase] pa_exact.t = t - tmp_exact_pressure = df.interpolate(pa_exact, subdomain.function_space[phase]) + tmp_exact_pressure = df.interpolate(pa_exact, subdomain.function_space["pressure"][phase]) tmp_exact_pressure.rename("exact_pressure_"+"{}".format(phase), "exactpressure_"+"{}".format(phase)) file.write(tmp_exact_pressure, t) t += self.timestep_size @@ -829,8 +829,8 @@ class LDDsimulation(object): def _init_interfaces(self, interface_def_points: tp.List[tp.List[df.Point]] = None,# adjacent_subdomains: tp.List[np.ndarray] = None) -> None: - """ generate interfaces and interface marker functions for suddomains and - set the internal lists used by the LDDsimulation class. + """ generate interface objects and interface marker functions for suddomains + used by the LDDsimulation class. This initialises a list of interfaces and global interface marker functions used by the LDDsimulation class. @@ -887,13 +887,14 @@ class LDDsimulation(object): # The reason is that we want to mark outer boundaries with the value 0. marker_value = self.interface[glob_interface_num].marker_value self.interface[glob_interface_num].mark(self.interface_marker, marker_value) - self.interface[glob_interface_num].init_interface_values(self.interface_marker, marker_value) + self.interface[glob_interface_num].init_facet_dictionaries(self.interface_marker, marker_value, subdomain_index=0) # print(self.interface[glob_interface_num]) if self.write2file['meshes_and_markers']: filepath = self.output_dir+"interface_marker_global.pvd" df.File(filepath) << self.interface_marker + def _init_subdomains(self) -> None: """ generate dictionary of opjects of class DomainPatch. @@ -991,7 +992,7 @@ class LDDsimulation(object): interpolation_degree = self.interpolation_degree for num, subdomain in self.subdomain.items(): - V = subdomain.function_space + V = subdomain.function_space["pressure"] # p0 contains both pw_0 and pnw_0 for subdomain[num] p0 = self.initial_conditions[num] for phase in subdomain.has_phases: @@ -1050,7 +1051,7 @@ class LDDsimulation(object): else: self.outerBC.update({subdom_index: dict()}) self.dirichletBC_dfExpression.update({subdom_index: dict()}) - V = subdomain.function_space + V = subdomain.function_space["pressure"] boundary_marker = subdomain.outer_boundary_marker # loop over all outer boundary parts. This is a bit of a brainfuck: # subdomain.outer_boundary gives a dictionary of the outer boundaries of the subdomain. @@ -1099,7 +1100,7 @@ class LDDsimulation(object): if np.fabs(time - self.starttime) < self.tol: pass else: - V = subdomain.function_space + V = subdomain.function_space["pressure"] boundary_marker = subdomain.outer_boundary_marker for boundary_index, boundary in enumerate(subdomain.outer_boundary): boundary_marker_value = boundary.marker_value diff --git a/LDDsimulation/boundary_and_interface.py b/LDDsimulation/boundary_and_interface.py index c8bca3c..eb5033a 100644 --- a/LDDsimulation/boundary_and_interface.py +++ b/LDDsimulation/boundary_and_interface.py @@ -13,6 +13,7 @@ dofs between subdomains. import dolfin as df import numpy as np import typing as tp +import copy as cp class BoundaryPart(df.SubDomain): @@ -240,15 +241,28 @@ class Interface(BoundaryPart): # global index ob the subdomain self.global_index = global_index self.marker_value = self.global_index+1 + # dictionary containing for each facet belonging to the interface (with + # respect to a subdomain mesh) the coordinates of the the vertices + # belonging to that facet. This dictionary is created by + # self.init_facet_dictionaries() + self.facet_to_vertex_coordinates = dict() + # dictionary containing maps mapping subdomain mesh indices of facets + # to the index of the facet in the global mesh and vice versa. + self.local_to_global_facet_map = dict() + self.global_to_local_facet_map = dict() + # dictionary saving per subdomain a list of outer normals per facet defined + # on a mesh passed to the method self.init_facet_dictionaries() which + # populates this dictionary. + self.outer_normals = dict() # pressure_values is a dictionary of dictionaries, one for each of the # adjacent subdomains, holding the dof values over the interface of the # pressure (solution) values for communication. It is initialised by the # method init_interface_values(). - self.pressure_values = None - self.pressure_values_prev = None + self.pressure_values = dict() + self.pressure_values_prev = dict() # dictionaries holding the forms for the decoupling terms - self.gli_term = None - self.gli_term_prev = None + self.gli_term = dict() + self.gli_term_prev = dict() # dictionary holding the current iteration number i for each of the adjacent # subdomains with index in adjacent_subdomains self.current_iteration = {# @@ -311,58 +325,231 @@ class Interface(BoundaryPart): # print(f"this means the following dofs are interface dofs:", np.nonzero(is_a_dof_on_interface)[0]) return np.nonzero(is_a_dof_on_interface)[0] + + def dofs_and_coordinates(self, function_space: df.FunctionSpace,# + interface_marker: df.MeshFunction, # + interface_marker_value: int, + debug: bool = True) -> tp.Dict[int, tp.Dict[int, np.ndarray]]: + """ for a given function space function_space, calculate the dof indices + and coordinates along the interface. + + # Parameters + function_space: #type df.functionSpace the function space of whitch + to calculate the dof_to_vertex_map + restriction from. + interface_marker: #type df.MeshFunction marker function marking edges of i_mesh + according to self.inside + interface_marker_value: #type int marker value of interface_marker + + # Output #type tp.Dict[int, tp.Dict[int, np.ndarray]] + the first key is the index of + the facet relative to the numbering + of the mesh that interface_marker + is defined on. The value to + that key is a dictionary + containing global (not cell) + dof indix and corresponding + coordinates of the dof as + key: value pairs. + + """ + dofmap = function_space.dofmap() + element = function_space.element() + mesh_entity_iterator = df.SubsetIterator(interface_marker, interface_marker_value) + dofs_and_coords = dict() + for mesh_entity in mesh_entity_iterator: + # this is the facet index relative to the Mesh + # that interface_marker is defined on. + facet_index = mesh_entity.index() + dofs_and_coords.update({facet_index: dict()}) + # because mesh_entity doesn't have a method to access coordinates we + # need to cast it into an acutal facet object. + for cell in df.cells(mesh_entity): + # we actually only have one facet contained in this mesh_entity + # but we still need to loop over „all“ facets since df.facets(mesh_entity) + # returns an iterator object. + facet_cell = cell + facet_cell_index = facet_cell.index() + + # the cell is a triangle and for each dof numbered locally for that + # cell, we have the coordinates in a numpy array. + facet_cell_dof_coordinates = element.tabulate_dof_coordinates(facet_cell) + # the cell dof map gives to each local (to the cell) numbering of the + # dofs the index of the dof realitve to the global dof numbering of + # space, i.e. the index to access the dof with function.vector()[index]. + facet_cell_dofmap = dofmap.cell_dofs(facet_cell_index) + + for local_dof_ind, dof_coord in enumerate(facet_cell_dof_coordinates): + # we only want to store the dofs that are on the boundary. The + # cell is a triangle and only has one facet that belongs to + # the interface. we want to store only dofs that lie on that particular + # facet + if self._is_on_boundary_part(dof_coord): + global_dof_ind = facet_cell_dofmap[local_dof_ind] + dofs_and_coords[facet_index].update({global_dof_ind: dof_coord}) + + + if debug: + print(f"newly defined facet_dof_to_coordinates_dictionary of interface[{interface_marker_value-1}]") + print(dofs_and_coords.items()) + return cp.deepcopy(dofs_and_coords) + + + def init_facet_dictionaries(self,# + interface_marker: df.MeshFunction,# + interface_marker_value: int, + subdomain_index: int, + debug: bool = True) -> None: + """ create dictionary for each index subdomain_index containing the + index of each facet belonging to the interface (with respect to the mesh of + the subdomain subdomain_index) as key and stores a + list of the coordinates of each vertex (numpy arrays) belonging to the facet as + values. + Additionally, create dictionary storing per facet the outer normals, i.e. + pairs {facet_index: facet.normal()}. + This is needed by SubDomain.calc_gl0_term() for accessing the normals. + + The MeshFunction interface_marker is supposed to be defined on the + subdomain mesh belonging to subdomain subdomain_index. + + # Parameters + interface_marker: #type df.MeshFunction marker function marking + edges of i_mesh + according to self.inside + interface_marker_value: #type int marker value of interface_marker + """ + self.facet_to_vertex_coordinates.update({subdomain_index: dict()}) + self.outer_normals.update({subdomain_index: dict()}) + # Get a subset iterator for mesh entities along the facets marked by + # interface_marker and value interface_marker_value. + mesh_entity_iterator = df.SubsetIterator(interface_marker, interface_marker_value) + for mesh_entity in mesh_entity_iterator: + # because mesh_entity doesn't have a method to access coordinates we + # need to cast it into an acutal facet object. + for f in df.facets(mesh_entity): + # we actually only have one facet contained in this mesh_entity + # but we still need to loop over „all“ facets since df.facets(mesh_entity) + # returns an iterator object. + facet = f + + facet_vertices = [] + for vertex in df.vertices(facet): + # we only need the x,y coordinates. vertex.point()[:] returns + # the array and cannot be sliced. the result can be indexed, hence, + # vertex.point()[:][0:2] + facet_vertices.append(vertex.point()[:][0:2]) + + self.facet_to_vertex_coordinates[subdomain_index].update( + {facet.index(): facet_vertices} + ) + if subdomain_index == 0: + self.outer_normals[subdomain_index].update( + {facet.index(): None} + ) + else: + self.outer_normals[subdomain_index].update( + {facet.index(): facet.normal()[:][0:2]} + ) + if debug: + print(f"newly defined facet_to_vertex_coordinates_dictionary[{subdomain_index}]") + print(self.facet_to_vertex_coordinates[subdomain_index].items()) + print(f"outer normals per facet are: ", self.outer_normals[subdomain_index].items()) + + + def init_facet_maps(self, subdomain_index: int, debug: bool = False) -> None: + """ calculate the mapping that maps the local numbering of interface + facets (w.r.t. the mesh of subdomain subdomain_index) to the global + index with respect to the global domain (subdomain 0)""" + if subdomain_index == 0: + raise RuntimeError("expeced a subdomain index != 0 for calculation", + "of local_to_global_facet_map.") + + self.local_to_global_facet_map.update({subdomain_index: dict()}) + self.global_to_local_facet_map.update({subdomain_index: dict()}) + global_facet2coord = self.facet_to_vertex_coordinates[0] + subdom_facet2coord = self.facet_to_vertex_coordinates[subdomain_index] + for local_facet_index, local_facet_vertex in subdom_facet2coord.items(): + for global_facet_index, global_facet_vertex in global_facet2coord.items(): + # vertices are not necessarily in the same order + vertices_are_equal = np.array_equal(local_facet_vertex[0], global_facet_vertex[0]) + vertices_are_equal += np.array_equal(local_facet_vertex[0], global_facet_vertex[1]) + vertices_are_equal += np.array_equal(local_facet_vertex[1], global_facet_vertex[0]) + vertices_are_equal += np.array_equal(local_facet_vertex[1], global_facet_vertex[1]) + both_vertices_are_equal = vertices_are_equal == 2 + if both_vertices_are_equal: + self.local_to_global_facet_map[subdomain_index].update( + {local_facet_index: global_facet_index} + ) + self.global_to_local_facet_map[subdomain_index].update( + {global_facet_index: local_facet_index} + ) + if debug: + print("local_to_global_facet_map: ", self.local_to_global_facet_map[subdomain_index].items()) + print("global_to_local_facet_map: ", self.global_to_local_facet_map[subdomain_index].items()) + def init_interface_values(self,# interface_marker: df.MeshFunction,# - interface_marker_value: int) -> None: + interface_marker_value: int, + function_space: tp.Dict[str, tp.Dict[str, df.FunctionSpace]], + subdomain_index: int, + has_phases: tp.List[str], + ) -> None: """ allocate dictionaries that are used to store the interface dof values of the pressure, previous pressure, gl decoupling term as well as previous gl update term respectively as pairs(parent_mesh_index: dof_value) - The MeshFunction interface_marker is supposed to be one defined on the - global mesh, so that the indices used in the dictionary are the global - vertex indices and not indices of some submesh. - # Parameters interface_marker: #type df.MeshFunction marker function marking edges of i_mesh according to self.inside interface_marker_value: #type int marker value of interface_marker """ - vertex_indices = self._vertex_indices(interface_marker, # - interface_marker_value,# - print_vertex_indices = False) - self.pressure_values = dict() - #self.pressure_values is a dictionay which contains for each index in - # adjacent_subdomains a dictionary for wetting and nonwetting phase with - # as a value yet another dictionary holding the interface values. phew. - self.pressure_values.update({self.adjacent_subdomains[0]: dict()}) - self.pressure_values.update({self.adjacent_subdomains[1]: dict()}) - self.pressure_values_prev = dict() - self.pressure_values_prev.update({self.adjacent_subdomains[0]: dict()}) - self.pressure_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()}) - # the gli_term will hold dofs of the gli terms for communications - # they are initiated with 0 - 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.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 + function_name = ["pressure", "gli"] + for function in function_name: + if function == "pressure": + self.pressure_values.update({subdomain_index: dict()}) + self.pressure_values_prev.update({subdomain_index: dict()}) + else: #function == "gli": + self.gli_term.update({subdomain_index: dict()}) + self.gli_term_prev.update({subdomain_index: dict()}) + + for phase in has_phases: + if function == "pressure": + self.pressure_values[subdomain_index].update({phase: dict()}) + self.pressure_values_prev[subdomain_index].update({phase: dict()}) + else: #function == "gli": + self.gli_term[subdomain_index].update({phase: dict()}) + self.gli_term_prev[subdomain_index].update({phase: dict()}) + + space = function_space[function][phase] + dof_coordinates = self.dofs_and_coordinates(space, interface_marker, interface_marker_value) + # the dof dictionaries are grouped by the global (with respect to the + # global mesh) facet index of each facet along the interface. + # each dof is then saved with its koordinates as tuples as keys. + for subdom_mesh_facet_index, facet_dof_coord_dict in dof_coordinates.items(): + global_mesh_facet_index = self.local_to_global_facet_map[subdomain_index][subdom_mesh_facet_index] + if function == "pressure": + self.pressure_values[subdomain_index][phase].update({global_mesh_facet_index: dict()}) + self.pressure_values_prev[subdomain_index][phase].update({global_mesh_facet_index: dict()}) + else: #function == "gli": + self.gli_term[subdomain_index][phase].update({global_mesh_facet_index: dict()}) + self.gli_term_prev[subdomain_index][phase].update({global_mesh_facet_index: dict()}) + + for dof_coord in facet_dof_coord_dict.values(): + # we want to use the coordinates as key in a dictionary. + # Because dof_coord is a numpy array and numpy array are + # not hashable, we convert the numpy array into a tuple, + # which in turn can then be used as a dict key. + coord_tupel = (dof_coord[0], dof_coord[1]) + if function == "pressure": + self.pressure_values[subdomain_index][phase][global_mesh_facet_index].update({coord_tupel: 0.0}) + self.pressure_values_prev[subdomain_index][phase][global_mesh_facet_index].update({coord_tupel: 0.0}) + else: #function == "gli": + self.gli_term[subdomain_index][phase][global_mesh_facet_index].update({coord_tupel: 0.0}) + self.gli_term_prev[subdomain_index][phase][global_mesh_facet_index].update({coord_tupel: 0.0}) + # end init_interface_values + def read_pressure_dofs(self, from_function: df.Function, # interface_dofs: np.array,# diff --git a/LDDsimulation/domainPatch.py b/LDDsimulation/domainPatch.py index 1d01e0d..bbf83b8 100644 --- a/LDDsimulation/domainPatch.py +++ b/LDDsimulation/domainPatch.py @@ -197,7 +197,7 @@ class DomainPatch(df.SubDomain): # Dictionary holding the exact solution expression if we have a case with exact solution. # Is set by LDDsimualtion._init_exact_solution_expression() self.pressure_exact = None - # Dictionary of FEM function of self.function_space holding the interface + # Dictionary of FEM function of self.function_space["pressure"] holding the interface # dofs of the previous iteration of the neighbouring pressure. This is used # to assemble the gli terms in the method self.calc_gli_term(). # self.neighbouring_interface_pressure = None @@ -209,6 +209,8 @@ class DomainPatch(df.SubDomain): self._init_dof_maps() self._init_markers() self._init_measures() + self._calc_interface_dof_indices_and_coordinates() + self._init_interface_values() self._calc_dof_indices_of_interfaces() self._calc_interface_has_common_dof_indices() self._calc_normal_vectors_norms_for_common_dofs() @@ -230,7 +232,7 @@ class DomainPatch(df.SubDomain): self.gli_function = dict() for phase in self.has_phases: self.gli_function.update( - {phase: df.Function(self.function_space[phase])} + {phase: df.Function(self.function_space["pressure"][phase])} ) # END constructor @@ -424,7 +426,7 @@ class DomainPatch(df.SubDomain): pnw_prev = self.pressure_prev_timestep['nonwetting'] pc_prev = pnw_prev - pw_prev - n = df.FacetNormal(self.mesh) + # n = df.FacetNormal(self.mesh) S = self.saturation for phase in self.has_phases: # viscosity @@ -467,28 +469,197 @@ class DomainPatch(df.SubDomain): # part of gl0 seperately at the intersection points of two # interfaces (more details in comment below). Therefor we save # the robin pressure part and the neumann flux part separately - robin_pressure = Lambda[phase]*pa_prev.vector() - # normal_flux = df.dot(flux, n).get_local() - # print("type(normal_flux)", type(normal_flux)) - V = self.function_space[phase] - degree = V.ufl_element().degree() - interface_mesh = df.restriction(self.interface_marker, interface.marker_value) - # W = VectorFunctionSpace(interface_mesh, 'Q', 0) - W = FunctionSpace(interface_mesh, 'Q', 0) + robin_pressure = Lambda[phase]*pa_prev.vector() #.vector() + + flux_function = df.project(flux, self.function_space["flux"][phase]) + flux_x, flux_y = flux_function.split() + flux_x_coeficients = flux_x.vector() + flux_y_coeficients = flux_y.vector() + # print(f"flux_x coefficients: ", flux_x_coef[:]) + flux_x_dof_map = flux_x.function_space().dofmap() + flux_y_dof_map = flux_y.function_space().dofmap() + flux_dofmap = self.dofmap["flux"][phase] + # flux_dofmap1 = flux_dofmap[0] + # flux_dofmap2 = flux_dofmap[0] + + gli_dofmap = self.dofmap["gli"][phase] + gl0 = df.Function(self.function_space["gli"][phase]) + + boundary_facets_and_cells = dict() + interface_entity_iterator = df.SubsetIterator(self.interface_marker, interface.marker_value) + for mesh_entity in interface_entity_iterator: + # facet_index = mesh_entity.global_index() + # global_facet_index = mesh_entity.global_index() + # print(f"facet_index = {facet_index}\n", + # f"global_facet_index = {global_facet_index}\n") + for f in df.facets(mesh_entity): + facet = f + # facet_index = f.global_index() + # global_facet_index = f.global_index() + # print(f"facet_index = {facet_index}\n", + # f"global_facet_index = {global_facet_index}\n") + + facet_cell_iterator = df.cells(facet) + # only one cell of the mesh contains facet, but we still + # have to loop, because df.cells(facet) returns an iterator + # for cells. + for facet_cell in facet_cell_iterator: + boundary_facets_and_cells.update({facet: facet_cell}) + # for facet in df.facets(self.mesh): + # if facet.exterior(): + # boundary_facets.update({facet: facet.normal()}) + + flux_x_element = flux_x.function_space().element() + flux_y_element = flux_y.function_space().element() + gli_element = gl0.function_space().element() + p_element = self.function_space["pressure"][phase].element() + for facet, facet_cell in boundary_facets_and_cells.items(): + facet_cell_index = facet_cell.index() + facet_index = facet.index() + global_facet_cell_index = facet_cell.global_index() + + print(f"facet_cell_index = {facet_cell_index}\n", + f"global_facet_cell_index = {global_facet_cell_index}") + # since the flux is constant on each cell, we can take no + # matter which dof to calculate F*n. + n = facet.normal()[:] + print(flux_x_element.tabulate_dof_coordinates(facet_cell)) + flux_x_facet_cell_dof_coordinates = flux_x_element.tabulate_dof_coordinates(facet_cell) + flux_y_facet_cell_dof_coordinates = flux_y_element.tabulate_dof_coordinates(facet_cell) + gli_facet_cell_dof_coordinates = gli_element.tabulate_dof_coordinates(facet_cell) + p_facet_cell_dof_coordinates = p_element.tabulate_dof_coordinates(facet_cell) + + gli_cell_dofmap = gli_dofmap.cell_dofs(facet_cell_index) + flux_x_cell_dofmap = flux_x_dof_map.cell_dofs(facet_cell_index) + flux_dofmap1_cell = flux_dofmap.cell_dofs(facet_cell_index) + + flux_y_cell_dofmap = flux_y_dof_map.cell_dofs(facet_cell_index) + print("flux_dofmap: ", flux_dofmap1_cell) + print("flux_x_dofmap: ", flux_x_cell_dofmap) + print("flux_y_dofmap: ", flux_y_cell_dofmap) + p_cell_dofmap = p_dofmap.cell_dofs(facet_cell_index) + print(f"gli_cell_dofmap = {gli_cell_dofmap}") + + facet_vertex_coordinates = [] + for vertex in df.vertices(facet): + # we don't need the 3d coordinates only x and y. + vertex_coord = vertex.point()[:][0:2] + facet_vertex_coordinates.append(vertex_coord) + print(f"type of vertex.point()[:]", type(vertex_coord)) + print('vertex is on interface:', interface._is_on_boundary_part(vertex_coord)) + print(vertex_coord) + + p_dofs_along_facet = dict() + for local_dof_ind, dof_coord in enumerate(p_facet_cell_dof_coordinates): + if interface._is_on_boundary_part(dof_coord): + p_dofs_along_facet.update({local_dof_ind: dof_coord}) + print(p_dofs_along_facet.items()) + + flux_x_dofs_along_facet = dict() + for local_dof_ind, dof_coord in enumerate(flux_x_facet_cell_dof_coordinates): + if interface._is_on_boundary_part(dof_coord): + flux_x_dofs_along_facet.update({local_dof_ind: dof_coord}) + print(flux_x_dofs_along_facet.items()) + + flux_y_dofs_along_facet = dict() + for local_dof_ind, dof_coord in enumerate(flux_y_facet_cell_dof_coordinates): + if interface._is_on_boundary_part(dof_coord): + flux_y_dofs_along_facet.update({local_dof_ind: dof_coord}) + print(flux_y_dofs_along_facet.items()) + + gli_dofs_along_facet = dict() + for local_dof_ind, dof_coord in enumerate(gli_facet_cell_dof_coordinates): + if interface._is_on_boundary_part(dof_coord): + gli_dofs_along_facet.update({local_dof_ind: dof_coord}) + print(gli_dofs_along_facet.items()) + + for local_dof_ind, gli_dof_coord in gli_dofs_along_facet.items(): + flux_x_dof_ind = -1 + for local_ind, coord in flux_x_dofs_along_facet.items(): + if np.array_equal(coord, gli_dof_coord): + flux_x_dof_ind = flux_x_cell_dofmap[local_ind] + + flux_y_dof_ind = -1 + for local_ind, coord in flux_y_dofs_along_facet.items(): + if np.array_equal(coord, gli_dof_coord): + flux_y_dof_ind = flux_y_cell_dofmap[local_ind] + + p_dof_ind = -1 + for local_ind, coord in p_dofs_along_facet.items(): + if np.array_equal(coord, gli_dof_coord): + p_dof_ind = p_cell_dofmap[local_ind] + + + print(f"local_dof_ind = {local_dof_ind} and type is {type(local_dof_ind)}") + print(f"cell_dof_map = {gli_cell_dofmap}") + gli_dof_ind = gli_cell_dofmap[local_dof_ind] + print(f"local_dof_ind = {local_dof_ind} and global ind is {gli_dof_ind}") + print(f"nomal vector components = [{n[0]}, {n[1]}]") + print(f"p_dof_ind = {p_dof_ind}") + gl0.vector()[gli_dof_ind] = flux_x.vector()[flux_x_dof_ind]*n[0] + flux_y.vector()[flux_y_dof_ind]*n[1] - robin_pressure[p_dof_ind] + print(f"gl0.vector()[gli_dof_ind] = {gl0.vector()[gli_dof_ind]}") + + + + # print(cell_dofs_flux_x) + # cell_dofs_flux_y = flux_y_dof_map.cell_dofs(facet_cell_index) + # cell_dofs_gli = gli_dofmap.cell_dofs(facet_cell_index) + # flux_x_coef = flux_x.vector() + # # print(f"flux_x coefficients: ", flux_x_coef[:]) + # flux_space_dof_map = flux_x.function_space().dofmap() + # print("local to global dofs:", flux_space.dofmap().tabulate_local_to_global_dofs()) + # print("dofmap index map:", flux_space.dofmap().index_map()) + # print("entity dofs:", flux_space.dofmap().entity_dofs(self.mesh, 2)) + # print("block size of dofmap:", flux_space.dofmap().block_size()) + # interface_facets = df.SubsetIterator(self.interface_marker, interface.marker_value) + # for facet in interface_facets: + # print(facet) + # print(facet.entities(0)) + # how_many_cells = 0 + # for cell in df.cells(facet): + # how_many_cells += 1 + # print(f"Cell containing facet {facet} has dofs", flux_space_dof_map.cell_dofs(cell.index())) + # print("entity dofs of facet:", flux_space_dof_map.entity_dofs(self.mesh, 2, [facet.entities(0)[1]])) + # # print("entity dofs of facet:", flux_space_dof_map.entity_dofs(self.mesh, 2, [facet.entities(0)[1]])) + # print(f"the facet {facet.index()} has {how_many_cells} cells") + # for vertex in df.vertices(facet): + # print(vertex.point()[:]) + # print(f"dofmap flux_space:", flux_space.dofmap().dofs()) + # print(f"cell dof flux_space:", flux_space.dofmap().cell_dofs(0)) + + # print(flux_space.tabulate_dof_coordinates()) + # print("Mesh Cells that contain a boundary facet (alt)") + # for mesh_cell in df.cells(self.mesh): + # for facet in df.facets(mesh_cell): + # if facet.exterior(): + # print(facet.entities(0)) + # for vertex in df.vertices(facet): + # print(vertex.point()[:]) + # print(mesh_cell, mesh_cell.get_vertex_coordinates()) + + + + + # flux_function = df.project(flux_x, W) # Project the normal flux onto V so that we can - normal_flux_Q0 = df.project(df.dot(flux,n), W) - flux_x, flux_y = flux_FEM.split(deepcopy=True) - normal_flux = normal_flux_FEM.vector() - interface_dofs = self._dof_indices_of_interface[interf_ind][phase] - parent = self.parent_mesh_index - d2v = self.dof2vertex[phase] - for dof_index in interface_dofs: - gli = normal_flux[dof_index] - robin_pressure[dof_index] - # from_array 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. - interface.gli_term_prev[subdomain_ind][phase].update( - {parent[d2v[dof_index]]: gli} - ) + # gl0 = df.project(df.dot(flux_function,n) - robin_pressure, gli_space) + ds = self.ds + marker_value = interface.marker_value + phi_a = self.testfunction[phase] + gl0_form = gl0*phi_a*ds(marker_value) + gl0_assembled = df.assemble(gl0_form) + # # flux_x, flux_y = flux_FEM.split(deepcopy=True) + # normal_flux = normal_flux_FEM.vector() + # interface_dofs = self._dof_indices_of_interface[interf_ind][phase] + # parent = self.parent_mesh_index + # d2v = self.dof2vertex[phase] + # for dof_index in interface_dofs: + # gli = normal_flux[dof_index] - robin_pressure[dof_index] + # # from_array 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. + # interface.gli_term_prev[subdomain_ind][phase].update( + # {parent[d2v[dof_index]]: gli} + # ) ### END calc_gl0_term @@ -510,7 +681,7 @@ class DomainPatch(df.SubDomain): subdomain = self.subdomain_index interface = self.interface[interface_index] # gli should be initialised by zero. - gli = df.Function(self.function_space[phase]) + gli = df.Function(self.function_space["pressure"][phase]) # neighbour index neighbour = interface.neighbour[subdomain] # update the current_iteration number of subdomain stored in the interface @@ -618,22 +789,32 @@ class DomainPatch(df.SubDomain): Note that P1 FEM is hard coded here, as it is assumed in other methods aswell. This method sets the class variable - self.function_space + self.function_space["pressure"] self.trialfunction self.pressure self.testfunction """ + function_name = ["pressure", "gli", "flux"] + self.function_space = dict() - self.trialfunction = dict() - self.testfunction = dict() - # self.neighbouring_interface_pressure = dict() - for phase in self.has_phases: - self.function_space.update({phase: df.FunctionSpace(self.mesh, 'P', 1)}) - self.trialfunction.update({phase: df.TrialFunction(self.function_space[phase])}) - self.testfunction.update({phase: df.TestFunction(self.function_space[phase])}) - # self.neighbouring_interface_pressure.update( - # {phase: df.interpolate(df.Constant(0), self.function_space[phase])} - # ) + for function in function_name: + self.function_space.update({function: dict()}) + if function == "pressure": + self.trialfunction = dict() + self.testfunction = dict() + # self.neighbouring_interface_pressure = dict() + for phase in self.has_phases: + if function == "pressure": + self.function_space[function].update({phase: df.FunctionSpace(self.mesh, 'P', 1)}) + self.trialfunction.update({phase: df.TrialFunction(self.function_space["pressure"][phase])}) + self.testfunction.update({phase: df.TestFunction(self.function_space["pressure"][phase])}) + elif function == "gli": + degree = self.function_space["pressure"][phase].ufl_element().degree() + self.function_space[function].update({phase: df.FunctionSpace(self.mesh, 'DG', degree)}) + else: + degree = self.function_space["pressure"][phase].ufl_element().degree() + self.function_space[function].update({phase: df.VectorFunctionSpace(self.mesh, 'DG', degree)}) + def _init_dof_maps(self) -> None: """ calculate dof maps and the vertex to parent map. @@ -649,15 +830,28 @@ class DomainPatch(df.SubDomain): # print(f"on subdomain{self.subdomain_index} parent_vertex_indices: {self.parent_mesh_index}") self.dof2vertex = dict() self.vertex2dof = dict() + self.dofmap = dict() + self.dofmap.update({"pressure": dict()}) + self.dofmap.update({"gli": dict()}) + self.dofmap.update({"flux": dict()}) for phase in self.has_phases: self.dof2vertex.update(# - {phase :df.dof_to_vertex_map(self.function_space[phase])}# + {phase :df.dof_to_vertex_map(self.function_space["pressure"][phase])}# ) # print(f"on subdomain{self.subdomain_index} dof2vertex: {self.dof2vertex[phase]}") self.vertex2dof.update(# - {phase :df.vertex_to_dof_map(self.function_space[phase])}# + {phase :df.vertex_to_dof_map(self.function_space["pressure"][phase])}# ) # print(f"on subdomain{self.subdomain_index} vertex2dof: {self.vertex2dof[phase]}") + self.dofmap["pressure"].update( + {phase: self.function_space["pressure"][phase].dofmap()} + ) + self.dofmap["gli"].update( + {phase: self.function_space["gli"][phase].dofmap()} + ) + self.dofmap["flux"].update( + {phase: self.function_space["flux"][phase].dofmap()} + ) def _init_markers(self) -> None: """ define boundary markers @@ -677,6 +871,12 @@ class DomainPatch(df.SubDomain): marker_value = self.interface[glob_index].marker_value # each interface gets marked with the global interface index. self.interface[glob_index].mark(self.interface_marker, marker_value) + # init facet_to_vertex_coordinates_dictionray for subdoaain mesh + self.interface[glob_index].init_facet_dictionaries( + interface_marker=self.interface_marker, + interface_marker_value=marker_value, + subdomain_index=self.subdomain_index) + self.interface[glob_index].init_facet_maps(self.subdomain_index) # create outerBoundary objects and mark outer boundaries with 1 # in case there is no outer boundary, self.outer_boundary_def_points is # None. @@ -712,9 +912,54 @@ class DomainPatch(df.SubDomain): # measure over the interfaces self.ds = df.Measure('ds', domain = self.mesh, subdomain_data = self.interface_marker) + + def _calc_interface_dof_indices_and_coordinates(self): + """ calculate dictionaries containing for each local facet index of + interfaces a dictionary containing {interface_dof_index: dof_coordinates} + """ + function_name = ["pressure", "gli", "flux"] + marker = self.interface_marker + self.interface_dof_indices_and_coordinates = dict() + for function in function_name: + self.interface_dof_indices_and_coordinates.update( + {function: dict()} + ) + function_space = self.function_space[function] + for interface_index in self.has_interface: + interface = self.interface[interface_index] + marker_value = interface.marker_value + self.interface_dof_indices_and_coordinates[function].update( + {interface_index: dict()} + ) + for phase in self.has_phases: + self.interface_dof_indices_and_coordinates[function][interface_index].update(# + {phase: interface.dofs_and_coordinates(function_space[phase], marker, marker_value)} + ) + + + def _init_interface_values(self): + """ allocate the dictionaries for each interface used to store the + interface values for communication. + """ + # we don't need to communicate flux values directly. + function_name = ["pressure", "gli"] + marker = self.interface_marker + + for interf in self.has_interface: + marker_value = self.interface[interf].marker_value + space = self.function_space + self.interface[interf].init_interface_values( + interface_marker=marker, + interface_marker_value=marker_value, + function_space=space, + subdomain_index=self.subdomain_index, + has_phases=self.has_phases, + ) + + def _calc_dof_indices_of_interfaces(self): """ calculate dof indices of each interface """ - V = self.function_space + V = self.function_space["pressure"] marker = self.interface_marker for ind in self.has_interface: # the marker value for the interfaces is always global interface index+1 @@ -850,7 +1095,7 @@ class DomainPatch(df.SubDomain): file.write(self.pressure[phase], t) # if self.pressure_exact: # self.pressure_exact[phase].t = t - # tmp_exact_pressure = df.interpolate(self.pressure_exact[phase], self.function_space[phase]) + # tmp_exact_pressure = df.interpolate(self.pressure_exact[phase], self.function_space["pressure"][phase]) # tmp_exact_pressure.rename("exact_pressure_"+"{}".format(phase), "exactpressure_"+"{}".format(phase)) # file.write(tmp_exact_pressure, t) else: @@ -860,7 +1105,7 @@ class DomainPatch(df.SubDomain): ) file.write(self.pressure[phase], i) # create function to copy the subsequent errors to - error = df.Function(self.function_space[phase]) + error = df.Function(self.function_space["pressure"][phase]) error.assign(self.pressure[phase] - self.pressure_prev_iter[phase]) error.rename("pi_"+"{}".format(phase)+"-pim1_"+"{}".format(phase), # "pressure(t="+"{}".format(t)+")_"+"{}".format(phase) diff --git a/RR-2-patch-test-case/RR-2-patch-test.py b/RR-2-patch-test-case/RR-2-patch-test.py index 14c9635..aa8aff2 100755 --- a/RR-2-patch-test-case/RR-2-patch-test.py +++ b/RR-2-patch-test-case/RR-2-patch-test.py @@ -86,7 +86,7 @@ isRichards = { } ##################### MESH ##################################################### -mesh_resolution = 10 +mesh_resolution = 4 ##################### TIME ##################################################### timestep_size = 0.01 number_of_timesteps = 100 -- GitLab