From 2dc9b5a4583c99c7132c5e9fa2d0f70c48bac78e Mon Sep 17 00:00:00 2001 From: David Seus <david.seus@ians.uni-stuttgart.de> Date: Fri, 3 May 2019 20:15:27 +0200 Subject: [PATCH] rewrite calc_gli_term() --- LDDsimulation/LDDsimulation.py | 5 +- LDDsimulation/domainPatch.py | 358 ++++++++++++--------------------- 2 files changed, 129 insertions(+), 234 deletions(-) diff --git a/LDDsimulation/LDDsimulation.py b/LDDsimulation/LDDsimulation.py index c746a95..764ac95 100644 --- a/LDDsimulation/LDDsimulation.py +++ b/LDDsimulation/LDDsimulation.py @@ -969,8 +969,11 @@ class LDDsimulation(object): subdomain.pressure_prev_iter[phase].assign(pa0_interpolated) subdomain.pressure_prev_timestep[phase].assign(pa0_interpolated) - # populate interface dictionaries with pressure values. + # populate interface dictionaries with pressure values. The calc_gli_method + # of each subdomain reads from the interface.pressure_values_prev + # and from interface.pressure_values dictionary so both need to be populated. subdomain.write_pressure_to_interfaces() + subdomain.write_pressure_to_interfaces(previous_iter = True) subdomain.write_solution_to_xdmf(# file = self.solution_file[num], # time = self.starttime,# diff --git a/LDDsimulation/domainPatch.py b/LDDsimulation/domainPatch.py index 42c97c0..d2e1bb6 100644 --- a/LDDsimulation/domainPatch.py +++ b/LDDsimulation/domainPatch.py @@ -215,24 +215,17 @@ class DomainPatch(df.SubDomain): # END constructor #### PUBLIC METHODS - def write_pressure_to_interfaces(self): + def write_pressure_to_interfaces(self, previous_iter: int = False): """ save the interface values of self.pressure to all neighbouring interfaces This method is used by the LDDsimulation.LDDsolver method. # parameters - initial_values bool Flag to toggle wether to write self.pressure - or self.pressure_prev_iter to the interface. - This is needed for the first ever iteration - at time = 0. + previous_iter bool Flag to toggle wether to write self.pressure + to interface.pressure_values or to + interface.pressure_values_prev """ - # if initial_values: - # # this assumes, that self.pressure_prev_iter has been set by - # # LDDsimulation._init_initial_values(). - # from_func = self.pressure_prev_iter - # else: from_func = self.pressure - for ind in self.has_interface: for phase in self.has_phases: self.interface[ind].read_pressure_dofs(from_function = from_func[phase], # @@ -240,8 +233,8 @@ class DomainPatch(df.SubDomain): dof_to_vert_map = self.dof2vertex[phase],# local_to_parent_vertex_map = self.parent_mesh_index,# phase = phase,# - subdomain_ind = self.subdomain_index) - + subdomain_ind = self.subdomain_index, + previous_iter = previous_iter) def governing_problem(self, phase: str) -> tp.Dict[str, fl.Form]: """ return the governing form and right hand side for phase phase as a dictionary. @@ -518,218 +511,85 @@ class DomainPatch(df.SubDomain): f"dofs on interface:\n{interface_dofs['wetting']}\n", f"dof2global_vertex_map:\n{self.parent_mesh_index[self.dof2vertex['wetting'][interface_dofs['wetting']]]}\n" f"and dofs common with other interfaces:\n{dofs_in_common_with_other_interfaces['wetting']}") - if iteration == 0: - n = df.FacetNormal(self.mesh) - p_prev_iter = self.pressure_prev_timestep - k = self.relative_permeability - S = self.saturation - mu = self.viscosity - - if not neighbour_iter_num == 0: - # copy the gl term from the neighbour first to use it in the - # next iteration - if debug: - print(f"Interface{ind}.gli_term_prev[{subdomain}]['wetting']=\n",interface.gli_term_prev[subdomain]['wetting']) - interface.gli_term_prev[subdomain].update(# - {'wetting': interface.gli_term[neighbour]['wetting'].copy()} - ) - # for glob_dof_ind, dof in interface.gli_term[neighbour]['wetting'].items(): - # interface.gli_term_prev[subdomain]['wetting'][glob_dof_ind] = dof + for phase in self.has_phases: + # in case phase == 'nonwetting' only proceed if the neighbouring + # subdomain does not assume a Richards model. + if (phase == 'wetting') or (not interface.neighbour_isRichards[subdomain]): + # read gli_prev term from the neighbour and write it to + # the gli_assembled array of the subdomain + # to be able to sum the gli terms of all interfaces together, + # we need a dummy vector to hold the dofs read from the interface. + gli_prev_of_neighbour = np.zeros(gli_assembled_tmp[phase].size, dtype = float) + interface.write_gli_dofs(to_array = gli_prev_of_neighbour, # + interface_dofs = interface_dofs[phase],# + dof_to_vert_map = self.dof2vertex[phase],# + local_to_parent_vertex_map = self.parent_mesh_index,# + phase = phase,# + subdomain_ind = neighbour,# + # this is set to True because we need gli_prev + previous_iter = True + ) if debug: - print(f"Interface{ind}.gli_term[{neighbour}]['wetting']=\n",interface.gli_term[neighbour]['wetting']) - print(f"Interface{ind}.gli_term_prev[{subdomain}]['wetting']=\n",interface.gli_term_prev[subdomain]['wetting']) - # in case we have two-phase and the neighbour is two-phase, two, - # we need to Additionally save the nonwetting values. - if not interface.neighbour_isRichards[subdomain] and not self.isRichards: - interface.gli_term_prev[subdomain].update(# - {'nonwetting': interface.gli_term[neighbour]['nonwetting'].copy()} - ) - - if self.isRichards: - # assuming that we normalised atmospheric pressure to zero, - # pc = -pw in the Richards case. - pc_prev = -p_prev_iter['wetting'] - else: - pw_prev = p_prev_iter['wetting'] - pnw_prev = p_prev_iter['nonwetting'] - pc_prev = pnw_prev - pw_prev - - for phase in self.has_phases: - # viscosity - mu_a = mu[phase] - # relative permeability - ka = k[phase] - # previous pressure iteration - pa_prev = p_prev_iter[phase] - # testfunction - phi_a = self.testfunction[phase] - if phase == 'wetting': - # the wetting phase is always present and we always need - # to calculate a gl0 term. - # TODO: add intrinsic permeabilty!!!! - # TODO: add gravitiy term!!!!! - flux = -1/mu_a*ka(S(pc_prev))*df.grad(pa_prev) - gl0 = df.dot(flux, n) - Lambda[phase]*pa_prev - gl0_assembled = df.assemble(dt*gl0*phi_a*ds).get_local() - # if dofs_in_common_with_other_interfaces[phase].size == 0 - # there are no dofs that lie on another interface and we can - # skip the following step. - if dofs_in_common_with_other_interfaces[phase].size != 0: - if debug: - print(f"the following dofs (phase = {phase}) of interface{ind} are in common with \n", - f" other interfaces: {dofs_in_common_with_other_interfaces[phase]}") - for common_dof in dofs_in_common_with_other_interfaces[phase]: - # from the standpoint of the subdomain we are currently on, - # each dof can belong maximally to two interfaces. On these - # vertices, common to two interfaces, the dofs of the - # corresponding gli terms of both interfaces are averaged - # to give one value for this vertex. Because we achieve this - # by adding the assembled gli terms together we have to - # multiply the dofs that appear in more than one interface - # by 1/2. - gl0_assembled[common_dof] = 0.5*gl0_assembled[common_dof] - # now, add the just assembled and corrected term to the Global - # gli_assembled_tmp vector - gli_assembled_tmp[phase] += gl0_assembled + print(f"read gli_prev_iter[{subdomain}]:\n", gli_prev_of_neighbour) + + # read previous neighbouring pressure values from interface and write them to + # the function pa_prev. We need to make a distinction first. + if neighbour_iter_num <= iteration: + # in case neighbour_iter_num == iteration the neighbour + # has not yet iterated beyond + # the iteration we are currently in, so + # interface.pressure_values holds the "previous pressure" + # we need to read from the interface to calculate gli. + # In case neighbour_iter_num < iteration the neighbour has + # already reached the error tolerance and has stopped iterating. + # We therefor read again the current and not the previous pressure + take_previous_pressure_from_interface = False + if neighbour_iter_num + 1 < iteration: + print(f"neighbour_iter_num = {neighbour_iter_num} for neighbour = {neighbour}\n", + f"and iteration = {iteration} on subdomain {subdomain}\n", + "It is expected that this case should only be happening if the \n", + f"neighbour (index {neighbour}) has already finished its iteration, i.e. reached\n", + f"the error criterion. In this case we should have {iteration} > {neighbour_iter_num}.\n", + "If this is not the case revisite DomainPatch.calc_gli_term().") else: - # phase == 'nonwetting' - # if we are in the two-phase case but our neighbour assumes - # Richards equation, we hase a zero communication term. - if interface.neighbour_isRichards[subdomain]: - # if the neighbour of our subdomain (with index self.subdomain_index) - # assumes a Richards model, we don't have gli terms for the - # nonwetting phase and assemble the terms as zero form. - pass - # gli_assembled_tmp[phase] += df.assemble(df.Constant(0)*ds).get_local() - else: - # in this case the neighbour assumes two-phase flow - # and we need to calculte a gl0 torm for the nonwetting - # phase. - # we need anothre flux in this case - flux = -1/mu_a*ka(1-S(pc_prev))*df.grad(pa_prev) - gl0 = (df.dot(flux, n) - Lambda[phase]*pa_prev) - gl0_assembled = df.assemble(dt*gl0*phi_a*ds).get_local() - - if dofs_in_common_with_other_interfaces[phase].size != 0: - for common_dof in dofs_in_common_with_other_interfaces[phase]: - # see previous case. - gl0_assembled[common_dof] = 0.5*gl0_assembled[common_dof] - # now, add the just assembled and corrected term to the Global - # gli_assembled_tmp vector - gli_assembled_tmp[phase] += gl0_assembled - - # writing out glo to the current iteration dictionary of the - # interface takes place after the loop, see below. - - # END for loop over phases - interface.current_iteration[subdomain] += 1 - - else: # iteration > 0 - # the wetting phase update is always to be calculated even if - # only Richards is assumed on the current patch or the neighbour - # only assumes the Richards equation. - - # in case neighbour_iter_num == iteration we need to first save - # the gli_term of the neighbour to gli_term_prev. - if neighbour_iter_num == iteration: - # in this case, first save the gli_terms of the neighbour to - # gli_prev_term of the current subdomain. This is because - # we assume, that the neighbouring patch has done a previous - # iteration and we need the value to calculate the gli term with - if debug: - print(f"in iteration {iteration} and neighbour iteration = {neighbour_iter_num}:\n") - print(f"Interface{ind}.gli_term_prev[{subdomain}]['wetting']=\n",interface.gli_term_prev[subdomain]['wetting']) - - interface.gli_term_prev[subdomain].update(# - {'wetting': interface.gli_term[neighbour]['wetting'].copy()} - ) - # print(colored(f"id(interface.gli_term_prev[{subdomain}][wetting])= {id(interface.gli_term_prev[subdomain]['wetting'])}","red")) - # print(colored(f"id(interface.gli_term[{neighbour}][wetting])= {id(interface.gli_term[neighbour]['wetting'])}","red")) - if debug: - print(f"Interface{ind}.gli_term[{neighbour}]['wetting']=\n",interface.gli_term[neighbour]['wetting']) - print(f"Interface{ind}.gli_term_prev[{subdomain}]['wetting']=\n",interface.gli_term_prev[subdomain]['wetting']) - - if not interface.neighbour_isRichards[subdomain] and not self.isRichards: - interface.gli_term_prev[subdomain].update(# - {'nonwetting': interface.gli_term[neighbour]['nonwetting'].copy()} - ) - - for phase in self.has_phases: - # in case phase == 'nonwetting' only proceed if the neighbouring - # subdomain does not assume a Richards model. - if (phase == 'wetting') or (not interface.neighbour_isRichards[subdomain]): - # then read gli_prev term from interface and write it to - # the gli_assembled array of the subdomain - # to be able to sum the gli terms of all interfaces together, - # we need a dummy vector to hold the dofs read from the interface. - if debug: - print(f"Dict: Interface{ind}.gli_term[{neighbour}]['wetting']=\n",interface.gli_term[neighbour]['wetting']) - print("should be the same as \n") - print(f"From Dict Interface{ind}.gli_term_prev[{subdomain}]['wetting']=\n",interface.gli_term_prev[subdomain]['wetting']) - gli_readout_tmp = np.zeros(gli_assembled_tmp[phase].size, dtype = float) - interface.write_gli_dofs(to_array = gli_readout_tmp, # - interface_dofs = interface_dofs[phase],# - dof_to_vert_map = self.dof2vertex[phase],# - local_to_parent_vertex_map = self.parent_mesh_index,# - phase = phase,# - subdomain_ind = subdomain,# - # this is set to True because we need gli_prev - previous_iter = True - ) - if debug: - print(f"read gli_prev_iter[{subdomain}]:\n", gli_readout_tmp) - - # read neighbouring pressure values from interface and write them to - # the function self.neighbouring_interface_pressure - pa_prev = df.interpolate(df.Constant(0), self.function_space[phase]) - interface.write_pressure_dofs(to_function = pa_prev,#self.neighbouring_interface_pressure[phase], # - interface_dofs = interface_dofs[phase],# - dof_to_vert_map = self.dof2vertex[phase],# - local_to_parent_vertex_map = self.parent_mesh_index,# - phase = phase,# - subdomain_ind = neighbour,# - previous_iter = False) - - # use the above to calculate the gli update with - # in the paper p^{i-1}_{3-l} - # pa_prev = self.neighbouring_interface_pressure[phase] - phi_a = self.testfunction[phase] - gli_pressure_part = df.assemble(-2*dt*Lambda[phase]*pa_prev*phi_a*ds) - gli_p_part = gli_pressure_part.get_local() - gli_prev_neighbour = gli_readout_tmp - gli = gli_p_part -gli_prev_neighbour - # check if there are shared dofs with another interface. - if dofs_in_common_with_other_interfaces[phase].size != 0: - for common_dof in dofs_in_common_with_other_interfaces[phase]: - # see previous case. - gli[common_dof] = 0.5*gli[common_dof] - # now, add the just assembled and corrected term to the Global - # gli_assembled_tmp vector - gli_assembled_tmp[phase] += gli - - if neighbour_iter_num -1 == iteration: - # gli_term_prev has been used by the above and can now safely - # be overwritten with the gli_term of the neighbour. - interface.gli_term_prev[subdomain].update(# - {'wetting': interface.gli_term[neighbour]['wetting'].copy()} - ) - - if not interface.neighbour_isRichards[subdomain] and not self.isRichards: - interface.gli_term_prev[subdomain].update(# - {'nonwetting': interface.gli_term[neighbour]['nonwetting'].copy()} - ) - - if neighbour_iter_num + 1 < iteration: - print(f"neighbour_iter_num = {neighbour_iter_num} for neighbour = {neighbour}\n", - f"and iteration = {iteration} on subdomain {subdomain}\n", - "It is expected that this case should only be happening if the \n", - f"neighbour (index {neighbour}) has already finished its iteration, i.e. reached\n", - f"the error criterion. In this case we should have {iteration} > {neighbour_iter_num}.\n", - "If this is not the case revisite DomainPatch.calc_gli_term().") - - # finally we are done and can step up the current iteration count. - interface.current_iteration[subdomain] += 1 + # the case we think here is neighbour_iter_num = iteration + 1. + # in this case the neighbour has iterated once beyond + # the iteration we are currently in, so + # interface.pressure_values_prev holds the "previous pressure" + # we need to read from the interface to calculate gli. + # neighbour_iter_num > iteration + 1 should not appear since + # we only stop to iterate if we reach error tolerance and no + # subdomain is left out while iterating. + take_previous_pressure_from_interface = True + if neighbour_iter_num > iteration + 1: + print(f"neighbour_iter_num = {neighbour_iter_num} for neighbour = {neighbour}\n", + f"and iteration = {iteration} on subdomain {subdomain}\n", + "This case should not be happening.") + pa_prev = df.interpolate(df.Constant(0), self.function_space[phase]) + interface.write_pressure_dofs(to_function = pa_prev,#self.neighbouring_interface_pressure[phase], # + interface_dofs = interface_dofs[phase],# + dof_to_vert_map = self.dof2vertex[phase],# + local_to_parent_vertex_map = self.parent_mesh_index,# + phase = phase,# + subdomain_ind = neighbour,# + previous_iter = take_previous_pressure_from_interface) + + # use the above to calculate the gli update with + # in the paper p^{i-1}_{3-l} + # pa_prev = self.neighbouring_interface_pressure[phase] + phi_a = self.testfunction[phase] + gli_pressure_part = df.assemble(-2*dt*Lambda[phase]*pa_prev*phi_a*ds) + gli_p_prev_part = gli_pressure_part.get_local() + gli = gli_p_prev_part - gli_prev_neighbour + # check if there are shared dofs with another interface. + if dofs_in_common_with_other_interfaces[phase].size != 0: + for common_dof in dofs_in_common_with_other_interfaces[phase]: + # see previous case. + gli[common_dof] = 0.5*gli[common_dof] + # now, add the just assembled and corrected term to the Global + # gli_assembled_tmp vector + gli_assembled_tmp[phase] += gli ### END for ind in self.has_interface: # save the result of the calculation to the subdomain and to the interface @@ -738,23 +598,55 @@ class DomainPatch(df.SubDomain): self.gli_assembled.update( {phase: gli_assembled_tmp[phase].copy()} ) + # communicate the newly calculated terms for ind in self.has_interface: # self._calc_gli_term_on(interface, iteration) interface = self.interface[ind] if debug: print(f"Interface{ind}.gli_term[{subdomain}][{phase}]=\n",interface.gli_term[subdomain][phase]) print(f"before writing to interface[{ind}] gli_assembled[{phase}]=\n",self.gli_assembled[phase]) - # write gli dofs to interface dicts for communiction - interface.read_gli_dofs(from_array = self.gli_assembled[phase], # - interface_dofs = interface_dofs[phase],# - dof_to_vert_map = self.dof2vertex[phase],# - local_to_parent_vertex_map = self.parent_mesh_index,# - phase = phase,# - subdomain_ind = subdomain,# - previous_iter = False - ) - if debug: - print(f"gli_terms after writing to interface[{ind}] Interface[{ind}].gli_term[{subdomain}][{phase}]=\n",interface.gli_term[subdomain][phase]) + # write gli dofs to interface dicts for communiction. We have to + # distinguish different cases. + if neighbour_iter_num == iteration: + # in this case, the neighbour has not done the next iteration + # and will need both pressure and gli_term_prev of the current + # subdomain. Therefor, we save the the calculated gli_terms to + # the gli_term dictionary of the current subdomain, not the + # gli_term_prev dictionary. + interface.read_gli_dofs(from_array = self.gli_assembled[phase], # + interface_dofs = interface_dofs[phase],# + dof_to_vert_map = self.dof2vertex[phase],# + local_to_parent_vertex_map = self.parent_mesh_index,# + phase = phase,# + subdomain_ind = subdomain,# + previous_iter = False + ) + if neighbour_iter_num -1 == iteration: + # in this case, the neighbour has done the next iteration already + # and will not need both pressure_prev and gli_term_prev of the current + # subdomain. Therefor, we save the the calculated gli_terms to + # the gli_term_prev dictionary of the current subdomain, not the + # gli_term dictionary. + interface.read_gli_dofs(from_array = self.gli_assembled[phase], # + interface_dofs = interface_dofs[phase],# + dof_to_vert_map = self.dof2vertex[phase],# + local_to_parent_vertex_map = self.parent_mesh_index,# + phase = phase,# + subdomain_ind = subdomain,# + previous_iter = True + ) + # Additionally, the gli_term of the neighbour is saved to + # gli_term currently and will be overwritten in the next step, + # if we don't save it to gli_term_prev of the neighbour. + # similarly for the pressure values. + if phase == 'wetting' or not interface.neighbour_isRichards[subdomain]: + interface.gli_term_prev[neighbour].update(# + {phase: interface.gli_term[neighbour][phase].copy()} + ) + interface.pressure_values_prev[neighbour][phase].update( + {phase: interface.pressure_values[neighbour][phase].copy()} + ) + interface.current_iteration[subdomain] += 1 ### END calc_gli_term def null_all_interface_iteration_numbers(self) -> None: -- GitLab