From 669ad7c71b69ab1b8e58be1b1b600e016528f305 Mon Sep 17 00:00:00 2001 From: Christoph Lehmann <christoph.lehmann@ufz.de> Date: Tue, 11 Sep 2018 11:53:38 +0200 Subject: [PATCH] [T] Python BC: also cover corner cases --- .../PythonHertzContact/hertz_contact_bc.py | 67 +++++++++++++++---- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/Tests/Data/Mechanics/Linear/PythonHertzContact/hertz_contact_bc.py b/Tests/Data/Mechanics/Linear/PythonHertzContact/hertz_contact_bc.py index 5351190b431..4abfa50b91b 100644 --- a/Tests/Data/Mechanics/Linear/PythonHertzContact/hertz_contact_bc.py +++ b/Tests/Data/Mechanics/Linear/PythonHertzContact/hertz_contact_bc.py @@ -3,15 +3,6 @@ from __future__ import print_function import OpenGeoSys -# enable/disable debug output -if True: - def debug(*args): - print(*args) -else: - def debug(*args): - pass - - SPHERE_RADIUS = 1.0 START_TIME = 0.0 @@ -23,6 +14,8 @@ class HertzContactBC(OpenGeoSys.BoundaryCondition): self._first_node = None # ID of the first node of this BC's geometry self._t_old = START_TIME - 1.0 # time of previous invocation of this BC + self._boundary_x_coords = [] # the x coordinates of all boundary nodes + def _init_timestep(self): """Initializes the internal state at the beginning of a new timestep.""" @@ -32,14 +25,18 @@ class HertzContactBC(OpenGeoSys.BoundaryCondition): self._max_x_with_y_excess = -1.0 # maximum x value where a node is above the contact line + self._tendency_is_up = True # whether the contact area is supposed to grow in the current iteration + self._a_curr = 0.0 # the radius of the contact area in this iteration (continuously updated) + self._a_prev = 0.0 # the radius of the contact area that was prescripted in the previous iteration + + self._iteration = 0 # the nonlinear solver iteration number + def _init_iteration(self): """Initializes the internal state at the beginning of a new nonlinear solver iteration.""" # variant of a bisection algorithm if self._max_x_with_y_excess >= 0.0: - # there were nodes above the contact area in the previous - # iteration => contact area has to grow. if self._max_x_with_y_excess < self._a_est: # This is an ad-hoc variation of the bisection algorithm for # finding the radius of the contact area. The variation is @@ -50,16 +47,27 @@ class HertzContactBC(OpenGeoSys.BoundaryCondition): # applied are off by one iteration. self._a_range[1] = self._max_x_with_y_excess assert self._a_range[0] <= self._a_range[1] + self._tendency_is_up = False else: + # there were nodes above the contact area in the previous + # iteration => contact area has to grow. self._a_range[0] = self._a_est + self._tendency_is_up = True else: # contact area has to shrink self._a_range[1] = self._a_est + self._tendency_is_up = False self._a_est = 0.5 * sum(self._a_range) - print("BC: a_est={:.4f}, ({:.4f}, {:.4f})".format(self._a_est, self._a_range[0], self._a_range[1])) self._max_x_with_y_excess = -1.0 + self._a_prev = self._a_curr + self._a_curr = -1.0 + + self._iteration += 1 + + print("BC: a_est={:.4f}, a_prev={:.4f} ({:.4f}, {:.4f})".format( + self._a_est, self._a_prev, self._a_range[0], self._a_range[1])) def getDirichletBCValue(self, t, coords, node_id, primary_vars): @@ -74,16 +82,21 @@ class HertzContactBC(OpenGeoSys.BoundaryCondition): if self._first_node == node_id: self._init_iteration() + if self._iteration == 2: + self._boundary_x_coords.sort() x, y, z = coords ux, uy = primary_vars + if not self._boundary_x_coords_are_initialized(t): + self._boundary_x_coords.append(x) + try: # check that we are at the outer boundary - assert abs(x**2 + y**2 + z**2 - 1.0) < 1e-15 + assert abs(x**2 + y**2 + z**2 - SPHERE_RADIUS**2) < 1e-15 except: print("assert abs(x**2 + y**2 + z**2 - 1.0) < 1e-15", - x, y, z, x**2 + y**2 + z**2 - 1) + x, y, z, x**2 + y**2 + z**2 - SPHERE_RADIUS**2) raise y_deformed = y + uy @@ -96,6 +109,28 @@ class HertzContactBC(OpenGeoSys.BoundaryCondition): if x <= self._a_est: res = (True, y_top - y) + self._a_curr = max(x, self._a_curr) + + elif self._tendency_is_up: + # This branch catches some corner-cases where Dirichlet BCs are + # set on too few nodes. + # + # The existence of these corner cases and the fact that the + # bisection algorithm for finding the correct contact radius is + # rather ad-hoc show that in general for similar problems it + # will be necessary to have more flexible convergence criteria + # or nonlinear solvers. In particular said similar problems are + # those where the surfaces where Dirichlet or Neumann BCs are + # set change for changing primary variables. + + if x <= self._a_prev: + assert False # this case shouldn't happen + res = (True, y_top - y) + elif self._boundary_x_coords_are_initialized(t): + idx = self._boundary_x_coords.index(x) + if idx != 0 and self._boundary_x_coords[idx-1] == self._a_prev: + res = (True, y_top - y) + return res @@ -107,5 +142,9 @@ class HertzContactBC(OpenGeoSys.BoundaryCondition): return 1.0 - 0.005 * t + def _boundary_x_coords_are_initialized(self, t): + return self._iteration >= 2 + + # instantiate the BC object used by OpenGeoSys bc = HertzContactBC() -- GitLab