Skip to content

Commit 97a915e

Browse files
authored
Improve GeometryCollection docs (#106)
* improve the docs for GeometryCollection a bit * fix failures after stricter GeometryCollection check
1 parent 40a3852 commit 97a915e

File tree

4 files changed

+103
-40
lines changed

4 files changed

+103
-40
lines changed

examples/fmm-error.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def main():
5858
"unaccel_qbx": unaccel_qbx,
5959
"qbx": unaccel_qbx.copy(fmm_order=10),
6060
"targets": PointsTarget(actx.freeze(actx.from_numpy(fplot.points)))
61-
})
61+
}, auto_where=("qbx", "targets"))
6262
density_discr = places.get_discretization("unaccel_qbx")
6363

6464
nodes = thaw(density_discr.nodes(), actx)

pytential/symbolic/execution.py

Lines changed: 95 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -634,39 +634,62 @@ class _GeometryCollectionConnectionCacheKey:
634634
class GeometryCollection:
635635
"""A mapping from symbolic identifiers ("place IDs", typically strings)
636636
to 'geometries', where a geometry can be a
637-
:class:`pytential.source.PotentialSource`
638-
or a :class:`pytential.target.TargetBase`.
637+
:class:`~pytential.source.PotentialSource`, a
638+
:class:`~pytential.target.TargetBase` or a
639+
:class:`~meshmode.discretization.Discretization`.
640+
639641
This class is meant to hold a specific combination of sources and targets
640642
serve to host caches of information derived from them, e.g. FMM trees
641643
of subsets of them, as well as related common subexpressions such as
642644
metric terms.
643645
646+
Refinement of :class:`pytential.qbx.QBXLayerPotentialSource` entries is
647+
performed on demand, i.e. on calls to :meth:`get_discretization` with
648+
a specific *discr_stage*. To perform refinement explicitly, call
649+
:func:`pytential.qbx.refinement.refine_geometry_collection`,
650+
which allows more customization of the refinement process through
651+
parameters.
652+
653+
.. automethod:: __init__
654+
655+
.. attribute:: auto_source
656+
657+
Default :class:`~pytential.symbolic.primitives.DOFDescriptor` for the
658+
source geometry.
659+
660+
.. attribute:: auto_target
661+
662+
Default :class:`~pytential.symbolic.primitives.DOFDescriptor` for the
663+
target geometry.
664+
644665
.. automethod:: get_geometry
645-
.. automethod:: get_connection
646666
.. automethod:: get_discretization
667+
.. automethod:: get_connection
647668
648669
.. automethod:: copy
649670
.. automethod:: merge
650671
651-
Refinement of :class:`pytential.qbx.QBXLayerPotentialSource` entries is
652-
performed on demand, or it may be performed by explcitly calling
653-
:func:`pytential.qbx.refinement.refine_geometry_collection`,
654-
which allows more customization of the refinement process through
655-
parameters.
656672
"""
657673

658674
def __init__(self, places, auto_where=None):
659-
"""
675+
r"""
660676
:arg places: a scalar, tuple of or mapping of symbolic names to
661677
geometry objects. Supported objects are
662678
:class:`~pytential.source.PotentialSource`,
663679
:class:`~pytential.target.TargetBase` and
664680
:class:`~meshmode.discretization.Discretization`. If this is
665681
a mapping, the keys that are strings must be valid Python identifiers.
666-
:arg auto_where: location identifier for each geometry object, used
667-
to denote specific discretizations, e.g. in the case where
668-
*places* is a :class:`~pytential.source.LayerPotentialSourceBase`.
669-
By default, we assume
682+
The tuple should contain only two entries, denoting the source and
683+
target geometries for layer potential evaluation, identified by
684+
*auto_where*.
685+
686+
:arg auto_where: a single or a tuple of two
687+
:class:`~pytential.symbolic.primitives.DOFDescriptor`\ s, or values
688+
that can be converted to one using
689+
:func:`~pytential.symbolic.primitives.as_dofdesc`. The two
690+
descriptors are used to define the default source and target
691+
geometries for layer potential evaluations.
692+
By default, they are set to
670693
:class:`~pytential.symbolic.primitives.DEFAULT_SOURCE` and
671694
:class:`~pytential.symbolic.primitives.DEFAULT_TARGET` for
672695
sources and targets, respectively.
@@ -705,6 +728,15 @@ def __init__(self, places, auto_where=None):
705728

706729
# {{{ validate
707730

731+
# check auto_where
732+
if auto_source.geometry not in self.places:
733+
raise ValueError("'auto_where' source geometry is not in the "
734+
f"collection: '{auto_source.geometry}'")
735+
736+
if auto_target.geometry not in self.places:
737+
raise ValueError("'auto_where' target geometry is not in the "
738+
f"collection: '{auto_target.geometry}'")
739+
708740
# check allowed identifiers
709741
for name in self.places:
710742
if not isinstance(name, str):
@@ -715,8 +747,9 @@ def __init__(self, places, auto_where=None):
715747
# check allowed types
716748
for p in self.places.values():
717749
if not isinstance(p, (PotentialSource, TargetBase, Discretization)):
718-
raise TypeError("Values in 'places' must be discretization, targets "
719-
"or layer potential sources.")
750+
raise TypeError(
751+
"Values in 'places' must be discretization, targets "
752+
f"or layer potential sources, got '{type(p).__name__}'")
720753

721754
# check ambient_dim
722755
from pytools import is_single_valued
@@ -746,8 +779,9 @@ def _get_discr_from_cache(self, geometry, discr_stage):
746779
key = (geometry, discr_stage)
747780

748781
if key not in cache:
749-
raise KeyError("cached discretization does not exist on '{}'"
750-
"for stage '{}'".format(geometry, discr_stage))
782+
raise KeyError(
783+
"cached discretization does not exist on '{geometry}'"
784+
"for stage '{discr_stage}'")
751785

752786
return cache[key]
753787

@@ -756,7 +790,8 @@ def _add_discr_to_cache(self, discr, geometry, discr_stage):
756790
key = (geometry, discr_stage)
757791

758792
if key in cache:
759-
raise RuntimeError("trying to overwrite the cache")
793+
raise RuntimeError("trying to overwrite the discretization cache of "
794+
f"'{geometry}' for stage '{discr_stage}'")
760795

761796
cache[key] = discr
762797

@@ -765,8 +800,8 @@ def _get_conn_from_cache(self, geometry, from_stage, to_stage):
765800
key = (geometry, from_stage, to_stage)
766801

767802
if key not in cache:
768-
raise KeyError("cached connection does not exist on '{}' "
769-
"from '{}' to '{}'".format(geometry, from_stage, to_stage))
803+
raise KeyError("cached connection does not exist on "
804+
f"'{geometry}' from stage '{from_stage}' to '{to_stage}'")
770805

771806
return cache[key]
772807

@@ -775,7 +810,8 @@ def _add_conn_to_cache(self, conn, geometry, from_stage, to_stage):
775810
key = (geometry, from_stage, to_stage)
776811

777812
if key in cache:
778-
raise RuntimeError("trying to overwrite the cache")
813+
raise RuntimeError("trying to overwrite the connection cache of "
814+
f"'{geometry}' from stage '{from_stage}' to '{to_stage}'")
779815

780816
cache[key] = conn
781817

@@ -801,19 +837,38 @@ def _get_qbx_discretization(self, geometry, discr_stage):
801837
# }}}
802838

803839
def get_connection(self, from_dd, to_dd):
840+
"""Construct a connection from *from_dd* to *to_dd* geometries.
841+
842+
:param from_dd: a :class:`~pytential.symbolic.primitives.DOFDescriptor`
843+
or a value that can be converted to one using
844+
:func:`~pytential.symbolic.primitives.as_dofdesc`.
845+
:param to_dd: as *from_dd*.
846+
847+
:returns: an object compatible with the
848+
:class:`~meshmode.discretization.connection.DiscretizationConnection`
849+
interface.
850+
"""
851+
804852
from pytential.symbolic.dof_connection import connection_from_dds
805853
return connection_from_dds(self, from_dd, to_dd)
806854

807855
def get_discretization(self, geometry, discr_stage=None):
808-
"""
809-
:arg dofdesc: a :class:`~pytential.symbolic.primitives.DOFDescriptor`
810-
specifying the desired discretization.
811-
812-
:return: a geometry object in the collection corresponding to the
813-
key *dofdesc*. If it is a
814-
:class:`~pytential.source.LayerPotentialSourceBase`, we look for
815-
the corresponding :class:`~meshmode.discretization.Discretization`
816-
in its attributes instead.
856+
"""Get the geometry or discretization in the collection.
857+
858+
If a specific QBX stage discretization is requested, refinement is
859+
performed on demand and cached for subsequent calls.
860+
861+
:param geometry: the identifier of the geometry in the collection.
862+
:param discr_stage: if the geometry is a
863+
:class:`~pytential.source.LayerPotentialSourceBase`, this denotes
864+
the QBX stage of the returned discretization. Can be one of
865+
:class:`~pytential.symbolic.primitives.QBX_SOURCE_STAGE1` (default),
866+
:class:`~pytential.symbolic.primitives.QBX_SOURCE_STAGE2` or
867+
:class:`~pytential.symbolic.primitives.QBX_SOURCE_QUAD_STAGE2`.
868+
869+
:returns: a geometry object in the collection or a
870+
:class:`~meshmode.discretization.Discretization` corresponding to
871+
*discr_stage*.
817872
"""
818873
if discr_stage is None:
819874
discr_stage = sym.QBX_SOURCE_STAGE1
@@ -830,13 +885,18 @@ def get_discretization(self, geometry, discr_stage=None):
830885
return discr
831886

832887
def get_geometry(self, geometry):
888+
"""
889+
:param geometry: the identifier of the geometry in the collection.
890+
"""
891+
833892
try:
834893
return self.places[geometry]
835894
except KeyError:
836-
raise KeyError("geometry not in the collection: '{}'".format(
837-
geometry))
895+
raise KeyError(f"geometry not in the collection: '{geometry}'")
838896

839897
def copy(self, places=None, auto_where=None):
898+
"""Get a shallow copy of the geometry collection."""
899+
840900
places = self.places if places is None else places
841901
return type(self)(
842902
places=places.copy(),
@@ -845,7 +905,7 @@ def copy(self, places=None, auto_where=None):
845905
def merge(self, places):
846906
"""Merges two geometry collections and returns the new collection.
847907
848-
:arg places: A :class:`dict` or :class:`GeometryCollection` to
908+
:param places: a :class:`dict` or :class:`GeometryCollection` to
849909
merge with the current collection. If it is empty, a copy of the
850910
current collection is returned.
851911
"""
@@ -859,10 +919,10 @@ def merge(self, places):
859919
return self.copy(places=new_places)
860920

861921
def __repr__(self):
862-
return "{}({})".format(type(self).__name__, repr(self.places))
922+
return "{type(self).__name__}({repr(self.places)})"
863923

864924
def __str__(self):
865-
return "{}({})".format(type(self).__name__, str(self.places))
925+
return "{type(self).__name__}({repr(self.places)})"
866926

867927
# }}}
868928

test/test_layer_pot.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ def test_off_surface_eval_vs_direct(actx_factory, do_plot=False):
187187
places = GeometryCollection({
188188
"direct_qbx": direct_qbx,
189189
"fmm_qbx": fmm_qbx,
190-
"target": ptarget})
190+
"target": ptarget,
191+
}, auto_where=("fmm_qbx", "target"))
191192

192193
direct_density_discr = places.get_discretization("direct_qbx")
193194
fmm_density_discr = places.get_discretization("fmm_qbx")
@@ -273,7 +274,8 @@ def test_single_plus_double_with_single_fmm(actx_factory, do_plot=False):
273274
places = GeometryCollection({
274275
"direct_qbx": direct_qbx,
275276
"fmm_qbx": fmm_qbx,
276-
"target": ptarget})
277+
"target": ptarget,
278+
}, auto_where=("fmm_qbx", "target"))
277279

278280
direct_density_discr = places.get_discretization("direct_qbx")
279281
fmm_density_discr = places.get_discretization("fmm_qbx")
@@ -413,7 +415,8 @@ def test_unregularized_off_surface_fmm_vs_direct(actx_factory):
413415
places = GeometryCollection({
414416
"unregularized_direct": direct,
415417
"unregularized_fmm": fmm,
416-
"targets": ptarget})
418+
"targets": ptarget,
419+
}, auto_where=("unregularized_fmm", "targets"))
417420

418421
# }}}
419422

test/test_tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def test_geometry_collection_caching(actx_factory):
145145

146146
# construct a geometry collection
147147
from pytential import GeometryCollection
148-
places = GeometryCollection(dict(zip(sources, lpots)))
148+
places = GeometryCollection(dict(zip(sources, lpots)), auto_where=sources[0])
149149
print(places.places)
150150

151151
# check on-demand refinement

0 commit comments

Comments
 (0)