From 67f23b13513ecf6fe1ca7f7c1b4dafb3d51ef425 Mon Sep 17 00:00:00 2001
From: David Seus <david.seus@ians.uni-stuttgart.de>
Date: Tue, 30 Apr 2019 11:04:34 +0200
Subject: [PATCH] clean _eval_sources(). check ids

check that source terms have different python ids.
---
 LDDsimulation/LDDsimulation.py | 195 ++++++++++++++++++---------------
 1 file changed, 104 insertions(+), 91 deletions(-)

diff --git a/LDDsimulation/LDDsimulation.py b/LDDsimulation/LDDsimulation.py
index 85ec4c1..ede0b8a 100644
--- a/LDDsimulation/LDDsimulation.py
+++ b/LDDsimulation/LDDsimulation.py
@@ -100,9 +100,9 @@ class LDDsimulation(object):
 
         ## Private variables
         # maximal number of L-iterations that the LDD solver uses.
-        self._max_iter_num = 5000
+        self._max_iter_num = 1
         # TODO rewrite this with regard to the mesh sizes
-        self.calc_tol = self.tol
+        # self.calc_tol = self.tol
         # list of timesteps that get analyed. Gets initiated by self._init_analyse_timesteps
         self.analyse_timesteps = None
         # The number of subdomains are counted by self.init_meshes_and_markers()
@@ -143,14 +143,14 @@ class LDDsimulation(object):
         ########## SOLVER DEFINITION AND PARAMETERS ########
         # print("\nLinear Algebra Backends:")
         # df.list_linear_algebra_backends()
-        # print("\nLinear Solver Methods:")
+        # # print("\nLinear Solver Methods:")
         # df.list_linear_solver_methods()
-        # print("\nPeconditioners for Krylov Solvers:")
+        # # print("\nPeconditioners for Krylov Solvers:")
         # df.list_krylov_solver_preconditioners()
 
         ### Define the linear solver to be used.
         self.solver = 'bicgstab' #'gmres'#'bicgstab' # biconjugate gradient stabilized method
-        self.preconditioner = 'hypre_amg' #'ilu'#'hypre_amg' # incomplete LU factorization
+        self.preconditioner = 'jacobi'#'hypre_amg' #'ilu'#'hypre_amg' # incomplete LU factorization
         # dictionary of solver parametrs. This is passed to self._init_subdomains,
         # where for each subdomain a sovler object of type self.solver is created
         # with these parameters.
@@ -161,6 +161,17 @@ class LDDsimulation(object):
             'maximum_iterations': 1000,
             'report': False
         }
+        # Dictionaries needed by the LDD Solver
+        # dictionary holding bool variables for each subdomain indicating wether
+        # minimal error has been achieved, i.e. the calculation can be considered
+        # done or not.
+        self._calc_isdone_for_subdomain = dict()
+        # dictionary holding for each subdomain a dictionary with bool variables
+        # for each phase indicating wether minimal error has been achieved, i.e.
+        # the calculation can be considered done or not. This dictionary is necessary,
+        # because it can happen, that one phase needs severly less iterations than
+        # the other to achieve the error criterion.
+        self._calc_isdone_for_phase = dict()
 
 
     def set_parameters(self,#
@@ -285,9 +296,9 @@ class LDDsimulation(object):
             # check if the solver should beanalised or not.
             if analyse_solver_at_timesteps is not None and np.isin(self.timestep_num, analyse_solver_at_timesteps, assume_unique=True):
                 print("analyising timestep ...")
-                self.LDDsolver(debug = False, analyse_timestep = True)
+                self.LDDsolver(debug = True, analyse_timestep = True)
             else:
-                self.LDDsolver(debug = False, analyse_timestep = False)
+                self.LDDsolver(debug = True, analyse_timestep = False)
 
             self.t += self.timestep_size
             self.timestep_num += 1
@@ -347,18 +358,16 @@ class LDDsimulation(object):
         if time is None:
             time = self.t
 
+        error_criterion = 1E-8#min(1E-9, subdomain.mesh.hmax()**2*subdomain.timestep_size)
+
         if analyse_timestep:
             # dictionary holding filename strings for each subdomain to store
             # iterations in, used to analyse the behaviour of the iterates within
             # the current time step.
             solution_over_iteration_within_timestep = dict()
             subsequent_errors_over_iteration = dict()
-        # dictionary holding bool variables for each subdomain indicating wether
-        # minimal error has been achieved, i.e. the calculation can be considered
-        # done or not.
-        subdomain_calc_isdone = dict()
         # before the iteration gets started all iteration numbers must be nulled
-        # and the subdomain_calc_isdone dictionary must be set to False
+        # and the self._calc_isdone_for_subdomain dictionary must be set to False
         for ind, subdomain in self.subdomain.items():
             # create xdmf files for writing out iterations if analyse_timestep is
             # given.
@@ -375,7 +384,7 @@ class LDDsimulation(object):
                 #     subsequent_errors_over_iteration[ind].update( {phase: dict()} )
                 #
 
-            subdomain_calc_isdone.update({ind: False})
+            self._calc_isdone_for_subdomain.update({ind: False})
             # reset all interface[has_interface].current_iteration[ind] = 0, for
             # index has_interface in subdomain.has_interface
             subdomain.null_all_interface_iteration_numbers()
@@ -386,18 +395,16 @@ class LDDsimulation(object):
             self.update_DirichletBC_dictionary(time = time, #
                 subdomain_index = ind,
                 )
-            # the following only needs to be done when time is not equal 0 since
-            # our initialisation functions already took care of setting what follows
-            # for the initial iteration step at time = 0.
-            if time > self.calc_tol:
-                # this is the beginning of the solver, so iteration must be set
-                # to 0.
-                subdomain.iteration_number = 0
-                for phase in subdomain.has_phases:
-                    # set the solution of the previous timestep as new prev_timestep pressure
-                    subdomain.pressure_prev_timestep[phase].assign(
-                        subdomain.pressure[phase]
-                    )
+            #     # this is the beginning of the solver, so iteration must be set
+            #     # to 0.
+            subdomain.iteration_number = 0
+            for phase in subdomain.has_phases:
+                # set the solution of the previous timestep as new prev_timestep pressure
+                subdomain.pressure_prev_timestep[phase].assign(
+                    subdomain.pressure[phase]
+                )
+                # All isdone flags need to be zero before we start iterating
+                self._calc_isdone_for_phase[ind].update({phase: False})
 
         ### actual iteration starts here
         # gobal stopping criterion for the iteration.
@@ -410,7 +417,7 @@ class LDDsimulation(object):
             for sd_index, subdomain in self.subdomain.items():
                 # check if the calculation on subdomain has already be marked as
                 # finished.
-                if not subdomain_calc_isdone[sd_index]:
+                if not self._calc_isdone_for_subdomain[sd_index]:
                     # set subdomain iteration to current iteration
                     subdomain.iteration_number = iteration
                     if debug:
@@ -430,6 +437,10 @@ class LDDsimulation(object):
                             subsequent_iter_error.update(
                                 {phase: error_calculated}
                                 )
+                            # set flag to stop solving for that phase if error_criterion is met.
+                            if subsequent_iter_error[phase] < error_criterion:
+                                self._calc_isdone_for_phase[sd_index].update({phase: True})
+
                             if debug:
                                 print(f"the subsequent error in iteration {iteration} for phase {phase} is {subsequent_iter_error[phase]}")
 
@@ -458,16 +469,18 @@ class LDDsimulation(object):
                         print("max(subsequent_iter_error.values()): ", max(subsequent_iter_error.values()))
                         total_subsequent_iter_error = max(subsequent_iter_error.values())
                         # check if error criterion has been met
-                        error_criterion = 1E-9#min(1E-9, subdomain.mesh.hmax()**2*subdomain.timestep_size)
+
                         if debug:
                             print(f" total_subsequent_error in iter {iteration} = {total_subsequent_iter_error }\n")
                             print(f"the maximum distance between any to points in a cell mesh.hmax() on subdomain{sd_index} is h={subdomain.mesh.hmax()}")
                             print(f"the minimal distance between any to points in a cell mesh.hmin() on subdomain{sd_index} is h={subdomain.mesh.hmin()}")
                             print(f"the error criterion is tol = {error_criterion}")
-                        if total_subsequent_iter_error < error_criterion:
+                        wetting_done = self._calc_isdone_for_phase[sd_index]['wetting']
+                        nonwetting_done = self._calc_isdone_for_phase[sd_index]['nonwetting']
+                        if nonwetting_done and wetting_done:
                             if debug:
                                 print(f"subdomain{sd_index} reachd error tol = {error_criterion} in iteration {iteration}.")
-                            subdomain_calc_isdone[sd_index] = True
+                            self._calc_isdone_for_subdomain[sd_index] = True
 
                     # prepare next iteration
                     # write the newly calculated solution to the inteface dictionaries
@@ -491,7 +504,7 @@ class LDDsimulation(object):
                     all_subdomains_are_done = True
                     # then check if all domains are done. If not, reset
                     # all_subdomains_are_done to False.
-                    for subdomain, isdone in subdomain_calc_isdone.items():
+                    for subdomain, isdone in self._calc_isdone_for_subdomain.items():
                         if not isdone:
                             all_subdomains_are_done = False
                     #TODO check if maybe I still need to do
@@ -522,61 +535,65 @@ class LDDsimulation(object):
                                                         to write out each iteration.
         """
         subdomain = self.subdomain[subdomain_index]
+        calc_isdone_for_phase = self._calc_isdone_for_phase[subdomain_index]
         iteration = subdomain.iteration_number
         # calculate the gli terms on the right hand side  and store
         subdomain.calc_gli_term()
 
         for phase in subdomain.has_phases:
-            # extract L-scheme form and rhs (without gli term) from subdomain.
-            governing_problem = subdomain.governing_problem(phase = phase)
-            form = governing_problem['form']
-            rhs_without_gli = governing_problem['rhs_without_gli']
-            # assemble the form and rhs
-            form_assembled = df.assemble(form)
-            rhs_without_gli_assembled = df.assemble(rhs_without_gli)
-            # access the assembled gli term for the rhs.
-            gli_term_assembled = subdomain.gli_assembled[phase]
-            # subdomain.calc_gli_term() asslembles gli but on the left hand side
-            # so gli_term_assembled needs to actually be subtracted from the rhs.
-            if debug and subdomain.mesh.num_cells() < 36:
-                print("\nSystem before applying outer boundary conditions:")
-                print(f"phase = {phase}: rhs_without_gli:\n", rhs_without_gli_assembled.get_local())
-            rhs_without_gli_assembled.set_local(rhs_without_gli_assembled.get_local() - gli_term_assembled)
-            rhs_assembled = rhs_without_gli_assembled
-
-            if debug and subdomain.mesh.num_cells() < 36:
-                # print(f"phase = {phase}: form_assembled:\n", form_assembled.array())
-                # print(f"phase = {phase}: rhs_without_gli:\n", rhs_without_gli_assembled.get_local())
-                print(f"phase = {phase}: gli_term:\n", gli_term_assembled)
-                print(f"phase = {phase}: rhs_assembled:\n", rhs_assembled.get_local())
-
-            # apply outer Dirichlet boundary conditions if present.
-            if subdomain.outer_boundary is not None:
-                # apply boundary dirichlet conditions for all outer boundaries.
-                for index, boundary in enumerate(subdomain.outer_boundary):
-                    self.outerBC[subdomain_index][index][phase].apply(form_assembled, rhs_assembled)
-
-            if debug and subdomain.mesh.num_cells() < 36:
-                print("\nSystem after applying outer boundary conditions:")
-                # print(f"phase = {phase}: form_assembled:\n", form_assembled.array())
-                print(f"phase = {phase}: rhs_assembled:\n", rhs_assembled.get_local())
-
-            # # set previous iteration as initial guess for the linear solver
-            # pi_prev = subdomain.pressure_prev_iter[phase].vector().get_local()
-            # subdomain.pressure[phase].vector().set_local(pi_prev)
-
-            if debug and subdomain.mesh.num_cells() < 36:
-                print("\npressure before solver:\n", subdomain.pressure[phase].vector().get_local())
-
-            linear_solver = subdomain.linear_solver
-            if linear_solver is None:
-                df.solve(form_assembled, subdomain.pressure[phase].vector(), rhs_assembled, self.solver, self.preconditioner)
-            else:
-                linear_solver.solve(form_assembled, subdomain.pressure[phase].vector(), rhs_assembled)
-
-            if debug and subdomain.mesh.num_cells() < 36:
-                print("\npressure after solver:\n", subdomain.pressure[phase].vector().get_local())
+            if not calc_isdone_for_phase[phase]:
+                # extract L-scheme form and rhs (without gli term) from subdomain.
+                governing_problem = subdomain.governing_problem(phase = phase)
+                form = governing_problem['form']
+                rhs_without_gli = governing_problem['rhs_without_gli']
+                # assemble the form and rhs
+                form_assembled = df.assemble(form)
+                rhs_without_gli_assembled = df.assemble(rhs_without_gli)
+                # access the assembled gli term for the rhs.
+                gli_term_assembled = subdomain.gli_assembled[phase]
+                # subdomain.calc_gli_term() asslembles gli but on the left hand side
+                # so gli_term_assembled needs to actually be subtracted from the rhs.
+                if debug and subdomain.mesh.num_cells() < 36:
+                    print("\nSystem before applying outer boundary conditions:")
+                    print(f"phase = {phase}: rhs_without_gli:\n", rhs_without_gli_assembled.get_local())
+                rhs_without_gli_assembled.set_local(rhs_without_gli_assembled.get_local() - gli_term_assembled)
+                rhs_assembled = rhs_without_gli_assembled
+
+                if debug and subdomain.mesh.num_cells() < 36:
+                    # print(f"phase = {phase}: form_assembled:\n", form_assembled.array())
+                    # print(f"phase = {phase}: rhs_without_gli:\n", rhs_without_gli_assembled.get_local())
+                    print(f"phase = {phase}: gli_term:\n", gli_term_assembled)
+                    print(f"phase = {phase}: rhs_assembled:\n", rhs_assembled.get_local())
+
+                # apply outer Dirichlet boundary conditions if present.
+                if subdomain.outer_boundary is not None:
+                    # apply boundary dirichlet conditions for all outer boundaries.
+                    for index, boundary in enumerate(subdomain.outer_boundary):
+                        self.outerBC[subdomain_index][index][phase].apply(form_assembled, rhs_assembled)
+
+                if debug and subdomain.mesh.num_cells() < 36:
+                    print("\nSystem after applying outer boundary conditions:")
+                    # print(f"phase = {phase}: form_assembled:\n", form_assembled.array())
+                    print(f"phase = {phase}: rhs_assembled:\n", rhs_assembled.get_local())
+
+                # # set previous iteration as initial guess for the linear solver
+                # pi_prev = subdomain.pressure_prev_iter[phase].vector().get_local()
+                # subdomain.pressure[phase].vector().set_local(pi_prev)
+
+                if debug and subdomain.mesh.num_cells() < 36:
+                    print("\npressure before solver:\n", subdomain.pressure[phase].vector().get_local())
+
+                linear_solver = subdomain.linear_solver
+                if linear_solver is None:
+                    df.solve(form_assembled, subdomain.pressure[phase].vector(), rhs_assembled, self.solver, self.preconditioner)
+                else:
+                    linear_solver.solve(form_assembled, subdomain.pressure[phase].vector(), rhs_assembled)
 
+                if debug and subdomain.mesh.num_cells() < 36:
+                    print("\npressure after solver:\n", subdomain.pressure[phase].vector().get_local())
+            else:
+                print(f"calculation for phase {phase} is already done in timestep = {self.timestep_num},  t = {self.t}. skipping ..." )
+                pass
 
     def _init_solution_files(self):
         """ set up solution files for saving the solution of the LDD scheme for
@@ -588,7 +605,7 @@ class LDDsimulation(object):
         for subdom_ind, subdomain in self.subdomain.items():
             tau = subdomain.timestep_size
             L = subdomain.L
-            h = subdomain.mesh.hmin()
+            h = subdomain.mesh.hmax()
             if subdomain.isRichards:
                 Lam = subdomain.lambda_param['wetting']
                 name1 ="subdomain" + "{}".format(subdom_ind)
@@ -836,7 +853,7 @@ class LDDsimulation(object):
             # marker value gets internally set to global_index+1.
             # The reason is that we want to mark outer boundaries with the value 0.
             self.interface[num].mark(self.interface_marker, self.interface[num].marker_value)
-            print("Global interface vertices:")
+            # print("Global interface vertices:")
             self.interface[num].init_interface_values(self.interface_marker, self.interface[num].marker_value)
 
         if self.write2file['meshes_and_markers']:
@@ -887,6 +904,8 @@ class LDDsimulation(object):
             # for parameter, value in self.linear_solver.parameters.items():
             #     print(f"parameter: {parameter} = {value}")
             # df.info(solver_param, True)
+            # setup self._calc_isdone_for_phase dictionary.
+            self._calc_isdone_for_phase.update({subdom_num: dict()})
 
 
 
@@ -1059,21 +1078,15 @@ class LDDsimulation(object):
 
         subdomain = self.subdomain[subdomain_index]
         for phase in subdomain.has_phases:
-            if np.abs(time-self.starttime) < self.calc_tol:
+            if np.abs(time-self.starttime) < self.tol:
                 # here t = 0 and we have to initialise the sources.
-                mesh = subdomain.mesh
-                # p0 contains both pw_0 and pnw_0 for subdomain[subdomain_index]
                 f = self.sources[subdomain_index][phase]
-                # note that the default interpolation degree is 2
-                f_expression = {
-                    phase: df.Expression(f, domain = mesh, degree = interpolation_degree, t = time)
-                    }
-
                 subdomain.source.update(
-                    {phase: f_expression[phase]}#
+                    {phase: df.Expression(f, domain = subdomain.mesh,#
+                                             degree = interpolation_degree, #
+                                             t = time)}
                     )
             else:
-                # note that the default interpolation degree is 2
                 subdomain.source[phase].t = time
 
     def _add_parameters_to_output_dirname(self):
-- 
GitLab