diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 57caec74f47a8cc99b8c94ba1c6dfd460097715f..2de6d958a74cc452ee8e130ec7fe534d99e5f8a2 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -32,6 +32,24 @@ Python 3 POCL:
reports:
junit: test/pytest.xml
+Python 3 Intel:
+ script:
+ - export PY_EXE=python3
+ - export EXTRA_INSTALL="pybind11 numpy mako"
+ - source /opt/enable-intel-cl.sh
+ - export PYOPENCL_TEST="intel(r):pu"
+ - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh
+ - ". ./build-and-test-py-project.sh"
+ tags:
+ - python3
+ - pocl
+ - mpi
+ except:
+ - tags
+ artifacts:
+ reports:
+ junit: test/pytest.xml
+
Python 2.7 POCL MPI:
script:
- export PY_EXE=python2.7
@@ -68,6 +86,25 @@ Python 3 POCL MPI:
reports:
junit: test/pytest.xml
+Python 3 Intel MPI:
+ script:
+ - export PY_EXE=python3
+ - source /opt/enable-intel-cl.sh
+ - export PYOPENCL_TEST="intel(r):pu"
+ - export EXTRA_INSTALL="pybind11 numpy mako mpi4py pymetis"
+ - export PYTEST_ADDOPTS="-k mpi"
+ - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh
+ - ". ./build-and-test-py-project.sh"
+ tags:
+ - python3
+ - pocl
+ - mpi
+ except:
+ - tags
+ artifacts:
+ reports:
+ junit: test/pytest.xml
+
Python 3 POCL Examples:
script:
- export PY_EXE=python3
@@ -82,6 +119,21 @@ Python 3 POCL Examples:
except:
- tags
+Python 3 Intel Examples:
+ script:
+ - export PY_EXE=python3
+ - source /opt/enable-intel-cl.sh
+ - export PYOPENCL_TEST="intel(r):pu"
+ - export EXTRA_INSTALL="pybind11 numpy mako mpi4py pyvisfile pymetis"
+ - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-py-project-and-run-examples.sh
+ - ". ./build-py-project-and-run-examples.sh"
+ tags:
+ - python3
+ - pocl
+ - large-node
+ except:
+ - tags
+
Documentation:
script:
- EXTRA_INSTALL="pybind11 numpy"
diff --git a/examples/advection/weak.py b/examples/advection/weak.py
index bd9d61ca3e2bbfd416a271ffbbabca22f366e3fd..6e30094d080033f225a9dbc45227e0d0c7665ad0 100644
--- a/examples/advection/weak.py
+++ b/examples/advection/weak.py
@@ -13,106 +13,135 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
residual": sym.make_sym_array( + "
residual": sym.make_sym_array( "input_residual", field_components), } diff --git a/grudge/execution.py b/grudge/execution.py index 948be131205034a2d0ddb597a11d2c8ad8f42327..fade7fca37f92d728c2573c82f7af9a8c8cbcbdf 100644 --- a/grudge/execution.py +++ b/grudge/execution.py @@ -283,6 +283,21 @@ class ExecutionMapper(mappers.Evaluator, # }}} + def map_signed_face_ones(self, expr): + face_discr = self.discrwb.discr_from_dd(expr.dd) + assert face_discr.dim == 0 + + all_faces_conn = self.discrwb.connection_from_dds("vol", expr.dd) + field = face_discr.empty(self.queue, allocator=self.bound_op.allocator) + field.fill(1) + + for grp in all_faces_conn.groups: + for batch in grp.batches: + i = batch.to_element_indices.with_queue(self.queue) + field[i] = (2.0 * (batch.to_element_face % 2) - 1.0) * field[i] + + return field + # }}} # {{{ instruction execution functions diff --git a/grudge/models/advection.py b/grudge/models/advection.py index 091b9870c4b82a3fde2fc21b84bf92e5ec62ab17..c3b142e411cd5fd7b34b17bce69e529ff565d316 100644 --- a/grudge/models/advection.py +++ b/grudge/models/advection.py @@ -74,7 +74,7 @@ class AdvectionOperatorBase(HyperbolicOperator): class StrongAdvectionOperator(AdvectionOperatorBase): def flux(self, u): - normal = sym.normal(u. dd, self.ambient_dim) + normal = sym.normal(u.dd, self.ambient_dim) v_dot_normal = sym.cse(self.v.dot(normal), "v_dot_normal") return u.int * v_dot_normal - self.weak_flux(u) @@ -142,7 +142,7 @@ class VariableCoefficientAdvectionOperator(HyperbolicOperator): def flux(self, u): - normal = sym.normal(u. dd, self.ambient_dim) + normal = sym.normal(u.dd, self.ambient_dim) surf_v = sym.interp("vol", u.dd)(self.v) diff --git a/grudge/symbolic/dofdesc_inference.py b/grudge/symbolic/dofdesc_inference.py index c44a7940b9ec142171a0b0a67885c041ec9d43cb..38c775127488b79624264439778c334fac29fcd3 100644 --- a/grudge/symbolic/dofdesc_inference.py +++ b/grudge/symbolic/dofdesc_inference.py @@ -183,6 +183,7 @@ class DOFDescInferenceMapper(RecursiveMapper, CSECachingMapperMixin): return expr.dd map_node_coordinate_component = map_ones + map_signed_face_ones = map_ones def map_call(self, expr): arg_dds = [ diff --git a/grudge/symbolic/mappers/__init__.py b/grudge/symbolic/mappers/__init__.py index 0e9b8e02d67b6f2b86d217d8c230263281d49a19..b277c4c99e97280e4f82be63cac8bbcbb00bee8e 100644 --- a/grudge/symbolic/mappers/__init__.py +++ b/grudge/symbolic/mappers/__init__.py @@ -213,6 +213,7 @@ class IdentityMapperMixin(LocalOpReducerMixin, FluxOpReducerMixin): map_function_symbol = map_grudge_variable map_ones = map_grudge_variable + map_signed_face_ones = map_grudge_variable map_node_coordinate_component = map_grudge_variable # }}} @@ -266,6 +267,7 @@ class DependencyMapper( return set() map_ones = _map_leaf + map_signed_face_ones = _map_leaf map_node_coordinate_component = _map_leaf @@ -1235,6 +1237,7 @@ class CollectorMixin(OperatorReducerMixin, LocalOpReducerMixin, FluxOpReducerMix map_function_symbol = map_constant map_ones = map_grudge_variable + map_signed_face_ones = map_grudge_variable map_node_coordinate_component = map_grudge_variable map_operator = map_grudge_variable diff --git a/grudge/symbolic/primitives.py b/grudge/symbolic/primitives.py index ea9e89f6bb6a607ec96944b45c3f7f8c5f8a3193..f8bff1bf2af1403a652f37a32995fd3a70b88f6a 100644 --- a/grudge/symbolic/primitives.py +++ b/grudge/symbolic/primitives.py @@ -420,6 +420,28 @@ class Ones(HasDOFDesc, ExpressionBase): mapper_method = intern("map_ones") +class _SignedFaceOnes(HasDOFDesc, ExpressionBase): + """Produces DoFs on a face that are :math:`-1` if their corresponding + face number is odd and :math:`+1` if it is even. + *dd* must refer to a 0D (point-shaped) trace domain. + This is based on the face order of + :meth:`meshmode.mesh.MeshElementGroup.face_vertex_indices`. + + .. note:: + + This is used as a hack to generate normals with the correct orientation + in 1D problems, and so far only intended for this particular use cases. + (If you can think of a better way, please speak up!) + """ + + def __init__(self, dd): + dd = as_dofdesc(dd) + assert dd.is_trace() + super(_SignedFaceOnes, self).__init__(dd) + + mapper_method = intern("map_signed_face_ones") + + # {{{ geometry data class DiscretizationProperty(HasDOFDesc, ExpressionBase): @@ -503,7 +525,7 @@ def parametrization_derivative(ambient_dim, dim=None, dd=None): dim = ambient_dim if dim == 0: - return MultiVector(np.array([1.])) + return MultiVector(np.array([_SignedFaceOnes(dd)])) from pytools import product return product( @@ -602,17 +624,30 @@ def mv_normal(dd, ambient_dim, dim=None): if dim is None: dim = ambient_dim - 1 - # Don't be tempted to add a sign here. As it is, it produces + # NOTE: Don't be tempted to add a sign here. As it is, it produces # exterior normals for positively oriented curves. - pder = ( - pseudoscalar(ambient_dim, dim, dd=dd) - / # noqa: W504 - area_element(ambient_dim, dim, dd=dd)) - return cse( - # Dorst Section 3.7.2 - pder << pder.I.inv(), - "normal", cse_scope.DISCRETIZATION) + pder = pseudoscalar(ambient_dim, dim, dd=dd) \ + / area_element(ambient_dim, dim, dd=dd) + + # Dorst Section 3.7.2 + mv = pder << pder.I.inv() + + if dim == 0: + # NOTE: when the mesh is 0D, we do not have a clear notion of + # `exterior normal`, so we just take the tangent of the 1D element, + # interpolate it to the element faces and make it signed + tangent = parametrization_derivative( + ambient_dim, dim=1, dd=DD_VOLUME) + tangent = tangent / sqrt(tangent.norm_squared()) + + from grudge.symbolic.operators import interp + interp = interp(DD_VOLUME, dd) + mv = MultiVector(np.array([ + mv.as_scalar() * interp(t) for t in tangent.as_vector() + ])) + + return cse(mv, "normal", cse_scope.DISCRETIZATION) def normal(dd, ambient_dim, dim=None, quadrature_tag=None): diff --git a/test/test_grudge.py b/test/test_grudge.py index 1d2732a9acfb9b51c0aadf4534ddb85d49d46995..65cd9d8975d49ebdd46c9029fd803aafb8089766 100644 --- a/test/test_grudge.py +++ b/test/test_grudge.py @@ -207,18 +207,18 @@ def test_2d_gauss_theorem(ctx_factory): @pytest.mark.parametrize(("mesh_name", "mesh_pars"), [ + ("segment", [8, 16, 32]), ("disk", [0.1, 0.05]), ("rect2", [4, 8]), ("rect3", [4, 6]), ]) @pytest.mark.parametrize("op_type", ["strong", "weak"]) -@pytest.mark.parametrize("flux_type", ["upwind"]) +@pytest.mark.parametrize("flux_type", ["central"]) @pytest.mark.parametrize("order", [3, 4, 5]) # test: 'test_convergence_advec(cl._csc, "disk", [0.1, 0.05], "strong", "upwind", 3)' def test_convergence_advec(ctx_factory, mesh_name, mesh_pars, op_type, flux_type, order, visualize=False): """Test whether 2D advection actually converges""" - cl_ctx = cl.create_some_context() queue = cl.CommandQueue(cl_ctx) @@ -226,7 +226,16 @@ def test_convergence_advec(ctx_factory, mesh_name, mesh_pars, op_type, flux_type eoc_rec = EOCRecorder() for mesh_par in mesh_pars: - if mesh_name == "disk": + if mesh_name == "segment": + from meshmode.mesh.generation import generate_box_mesh + mesh = generate_box_mesh( + [np.linspace(-1.0, 1.0, mesh_par)], + order=order) + + h = 2.0 / mesh_par + dim = 1 + dt_factor = 1.0 + elif mesh_name == "disk": pytest.importorskip("meshpy") from meshpy.geometry import make_circle, GeometryBuilder @@ -244,7 +253,6 @@ def test_convergence_advec(ctx_factory, mesh_name, mesh_pars, op_type, flux_type h = np.sqrt(mesh_par) dim = 2 dt_factor = 4 - elif mesh_name.startswith("rect"): dim = int(mesh_name[4:]) from meshmode.mesh.generation import generate_regular_rect_mesh