Skip to content

Commit e6de687

Browse files
committed
Merge branch 'main' into dcoll-refactor
2 parents 688b56b + 9f587ca commit e6de687

21 files changed

+405
-143
lines changed

examples/advection/surface.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,11 @@ def main(ctx_factory, dim=2, order=4, use_quad=False, visualize=False):
146146
qtag = None
147147

148148
from meshmode.discretization.poly_element import \
149-
PolynomialWarpAndBlendGroupFactory, \
149+
default_simplex_group_factory, \
150150
QuadratureSimplexGroupFactory
151151

152152
discr_tag_to_group_factory[dof_desc.DISCR_TAG_BASE] = \
153-
PolynomialWarpAndBlendGroupFactory(order)
153+
default_simplex_group_factory(base_dim=dim-1, order=order)
154154

155155
if use_quad:
156156
discr_tag_to_group_factory[qtag] = \
@@ -203,7 +203,8 @@ def rhs(t, u):
203203

204204
# {{{ time stepping
205205

206-
dt = adv_operator.estimate_rk4_timestep(dcoll, fields=u0)
206+
# FIXME: dt estimate is not necessarily valid for surfaces
207+
dt = 0.45 * adv_operator.estimate_rk4_timestep(actx, dcoll, fields=u0)
207208
nsteps = int(final_time // dt) + 1
208209

209210
logger.info("dt: %.5e", dt)

examples/advection/var-velocity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ def zero_inflow_bc(dtag, t=0):
195195
def rhs(t, u):
196196
return adv_operator.operator(t, u)
197197

198-
dt = adv_operator.estimate_rk4_timestep(dcoll, fields=u)
198+
dt = adv_operator.estimate_rk4_timestep(actx, dcoll, fields=u)
199199

200200
logger.info("Timestep size: %g", dt)
201201

examples/advection/weak.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def u_analytic(x, t=0):
164164
def rhs(t, u):
165165
return adv_operator.operator(t, u)
166166

167-
dt = adv_operator.estimate_rk4_timestep(dcoll, fields=u)
167+
dt = adv_operator.estimate_rk4_timestep(actx, dcoll, fields=u)
168168

169169
logger.info("Timestep size: %g", dt)
170170

examples/maxwell/cavities.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def cavity_mode(x, t=0):
9191
def rhs(t, w):
9292
return maxwell_operator.operator(t, w)
9393

94-
dt = maxwell_operator.estimate_rk4_timestep(dcoll, fields=fields)
94+
dt = maxwell_operator.estimate_rk4_timestep(actx, dcoll, fields=fields)
9595

9696
dt_stepper = set_up_rk4("w", dt, fields, rhs)
9797

examples/wave/var-propagation-speed.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,7 @@ def source_f(actx, dcoll, t=0):
102102
def rhs(t, w):
103103
return wave_op.operator(t, w)
104104

105-
dt_scaling_const = 2/3
106-
dt = dt_scaling_const * wave_op.estimate_rk4_timestep(dcoll, fields=fields)
107-
105+
dt = 2/3 * wave_op.estimate_rk4_timestep(actx, dcoll, fields=fields)
108106
dt_stepper = set_up_rk4("w", dt, fields, rhs)
109107

110108
final_t = 1

examples/wave/wave-min-mpi.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,7 @@ def source_f(actx, dcoll, t=0):
116116
[dcoll.zeros(actx) for i in range(dcoll.dim)]
117117
)
118118

119-
dt_scaling_const = 2/3
120-
dt = dt_scaling_const * wave_op.estimate_rk4_timestep(dcoll, fields=fields)
119+
dt = 2/3 * wave_op.estimate_rk4_timestep(actx, dcoll, fields=fields)
121120

122121
wave_op.check_bc_coverage(local_mesh)
123122

examples/wave/wave-op-mpi.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,12 @@ def rk4_step(y, t, h, f):
114114
return y + h/6*(k1 + 2*k2 + 2*k3 + k4)
115115

116116

117-
def estimate_rk4_timestep(dcoll, c):
118-
from grudge.dt_utils import (dt_non_geometric_factor,
119-
dt_geometric_factors)
117+
def estimate_rk4_timestep(actx, dcoll, c):
118+
from grudge.dt_utils import characteristic_lengthscales
120119

121-
dt_factor = (dt_non_geometric_factor(dcoll)
122-
* op.nodal_min(dcoll, "vol", dt_geometric_factors(dcoll)))
120+
local_dts = characteristic_lengthscales(actx, dcoll) / c
123121

124-
return dt_factor * (1 / c)
122+
return op.nodal_min(dcoll, "vol", local_dts)
125123

126124

127125
def bump(actx, dcoll, t=0):
@@ -187,8 +185,7 @@ def main(ctx_factory, dim=2, order=3, visualize=False):
187185
)
188186

189187
c = 1
190-
dt_scaling_const = 0.45
191-
dt = dt_scaling_const * estimate_rk4_timestep(dcoll, c)
188+
dt = 0.45 * estimate_rk4_timestep(actx, dcoll, c)
192189

193190
vis = make_visualizer(dcoll)
194191

examples/wave/wave-op-var-velocity.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,12 @@ def rk4_step(y, t, h, f):
129129
return y + h/6*(k1 + 2*k2 + 2*k3 + k4)
130130

131131

132-
def estimate_rk4_timestep(dcoll, c):
133-
from grudge.dt_utils import (dt_non_geometric_factor,
134-
dt_geometric_factors)
132+
def estimate_rk4_timestep(actx, dcoll, c):
133+
from grudge.dt_utils import characteristic_lengthscales
135134

136-
dt_factor = (dt_non_geometric_factor(dcoll)
137-
* op.nodal_min(dcoll, "vol", dt_geometric_factors(dcoll)))
135+
local_dts = characteristic_lengthscales(actx, dcoll) / c
138136

139-
return dt_factor * (1 / c)
137+
return op.nodal_min(dcoll, "vol", local_dts)
140138

141139

142140
def bump(actx, dcoll, t=0, width=0.05, center=None):
@@ -182,15 +180,14 @@ def main(ctx_factory, dim=2, order=3, visualize=False):
182180
dcoll = make_discretization_collection(
183181
actx, mesh,
184182
discr_tag_to_group_factory={
185-
DISCR_TAG_BASE: PolynomialWarpAndBlendGroupFactory(order),
183+
DISCR_TAG_BASE: default_simplex_group_factory(base_dim=dim, order=order),
186184
DISCR_TAG_QUAD: QuadratureSimplexGroupFactory(3*order),
187185
}
188186
)
189187

190188
# bounded above by 1
191189
c = 0.2 + 0.8*bump(actx, dcoll, center=np.zeros(3), width=0.5)
192-
dt_scaling_const = 0.5
193-
dt = dt_scaling_const * estimate_rk4_timestep(dcoll, c=1)
190+
dt = 0.5 * estimate_rk4_timestep(actx, dcoll, c=1)
194191

195192
fields = flat_obj_array(
196193
bump(actx, dcoll, ),

grudge/discretization.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ def make_discretization_collection(
581581
:returns: A :class:`DiscretizationCollection`.
582582
"""
583583
from meshmode.discretization.poly_element import \
584-
PolynomialWarpAndBlendGroupFactory
584+
default_simplex_group_factory
585585

586586
if discr_tag_to_group_factory is None:
587587
if order is None:
@@ -591,7 +591,8 @@ def make_discretization_collection(
591591

592592
# Default choice: warp and blend simplex element group
593593
discr_tag_to_group_factory = {
594-
DISCR_TAG_BASE: PolynomialWarpAndBlendGroupFactory(order=order)
594+
DISCR_TAG_BASE: default_simplex_group_factory(base_dim=mesh.dim,
595+
order=order)
595596
}
596597
else:
597598
if order is not None:
@@ -603,7 +604,7 @@ def make_discretization_collection(
603604
)
604605

605606
discr_tag_to_group_factory[DISCR_TAG_BASE] = \
606-
PolynomialWarpAndBlendGroupFactory(order=order)
607+
default_simplex_group_factory(base_dim=mesh.dim, order=order)
607608

608609
# Modal discr should always comes from the base discretization
609610
discr_tag_to_group_factory[DISCR_TAG_MODAL] = \

grudge/dt_utils.py

Lines changed: 83 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
"""Helper functions for estimating stable time steps for RKDG methods.
22
3-
.. autofunction:: dt_non_geometric_factor
3+
Characteristic lengthscales
4+
---------------------------
5+
6+
.. autofunction:: characteristic_lengthscales
7+
8+
Non-geometric quantities
9+
------------------------
10+
11+
.. autofunction:: dt_non_geometric_factors
12+
13+
Mesh size utilities
14+
-------------------
15+
416
.. autofunction:: dt_geometric_factors
517
.. autofunction:: h_max_from_volume
618
.. autofunction:: h_min_from_volume
@@ -33,7 +45,7 @@
3345

3446
import numpy as np
3547

36-
from arraycontext import FirstAxisIsElementsTag
48+
from arraycontext import ArrayContext, FirstAxisIsElementsTag, thaw, freeze
3749

3850
from grudge.dof_desc import DD_VOLUME, DOFDesc, as_dofdesc
3951
from grudge.discretization import DiscretizationCollection
@@ -42,25 +54,75 @@
4254

4355
from meshmode.dof_array import DOFArray
4456

45-
from pytools import memoize_on_first_arg
57+
from pytools import memoize_on_first_arg, memoize_in
58+
59+
60+
def characteristic_lengthscales(
61+
actx: ArrayContext, dcoll: DiscretizationCollection) -> DOFArray:
62+
r"""Computes the characteristic length scale :math:`h_{\text{loc}}` at
63+
each node. The characteristic length scale is mainly useful for estimating
64+
the stable time step size. E.g. for a hyperbolic system, an estimate of the
65+
stable time step can be estimated as :math:`h_{\text{loc}} / c`, where
66+
:math:`c` is the characteristic wave speed. The estimate is obtained using
67+
the following formula:
68+
69+
.. math::
70+
71+
h_{\text{loc}} = \operatorname{min}\left(\Delta r_i\right) r_D
72+
73+
where :math:`\operatorname{min}\left(\Delta r_i\right)` is the minimum
74+
node distance on the reference cell (see :func:`dt_non_geometric_factors`),
75+
and :math:`r_D` is the inradius of the cell (see :func:`dt_geometric_factors`).
76+
77+
:returns: a frozen :class:`~meshmode.dof_array.DOFArray` containing a
78+
characteristic lengthscale for each element, at each nodal location.
79+
80+
.. note::
81+
82+
While a prediction of stability is only meaningful in relation to a given
83+
time integrator with a known stability region, the lengthscale provided here
84+
is not intended to be specific to any one time integrator, though the
85+
stability region of standard four-stage, fourth-order Runge-Kutta
86+
methods has been used as a guide. Any concrete time integrator will
87+
likely require scaling of the values returned by this routine.
88+
"""
89+
@memoize_in(dcoll, (characteristic_lengthscales,
90+
"compute_characteristic_lengthscales"))
91+
def _compute_characteristic_lengthscales():
92+
return freeze(
93+
DOFArray(
94+
actx,
95+
data=tuple(
96+
# Scale each group array of geometric factors by the
97+
# corresponding group non-geometric factor
98+
cng * geo_facts
99+
for cng, geo_facts in zip(
100+
dt_non_geometric_factors(dcoll),
101+
thaw(dt_geometric_factors(dcoll), actx)
102+
)
103+
)
104+
)
105+
)
106+
return thaw(_compute_characteristic_lengthscales(), actx)
46107

47108

48109
@memoize_on_first_arg
49-
def dt_non_geometric_factor(dcoll: DiscretizationCollection, dd=None) -> float:
50-
r"""Computes the non-geometric scale factor following [Hesthaven_2008]_,
51-
section 6.4:
110+
def dt_non_geometric_factors(
111+
dcoll: DiscretizationCollection, dd=None) -> list:
112+
r"""Computes the non-geometric scale factors following [Hesthaven_2008]_,
113+
section 6.4, for each element group in the *dd* discretization:
52114
53115
.. math::
54116
55117
c_{ng} = \operatorname{min}\left( \Delta r_i \right),
56118
57119
where :math:`\Delta r_i` denotes the distance between two distinct
58-
nodes on the reference element.
120+
nodal points on the reference element.
59121
60122
:arg dd: a :class:`~grudge.dof_desc.DOFDesc`, or a value convertible to one.
61123
Defaults to the base volume discretization if not provided.
62-
:returns: a :class:`float` denoting the minimum node distance on the
63-
reference element.
124+
:returns: a :class:`list` of :class:`float` values denoting the minimum
125+
node distance on the reference element for each group.
64126
"""
65127
if dd is None:
66128
dd = DD_VOLUME
@@ -88,8 +150,7 @@ def dt_non_geometric_factor(dcoll: DiscretizationCollection, dd=None) -> float:
88150
)
89151
)
90152

91-
# Return minimum over all element groups in the discretization
92-
return min(min_delta_rs)
153+
return min_delta_rs
93154

94155

95156
# {{{ Mesh size functions
@@ -156,7 +217,6 @@ def h_min_from_volume(
156217
) ** (1.0 / dim)
157218

158219

159-
@memoize_on_first_arg
160220
def dt_geometric_factors(
161221
dcoll: DiscretizationCollection, dd=None) -> DOFArray:
162222
r"""Computes a geometric scaling factor for each cell following [Hesthaven_2008]_,
@@ -175,8 +235,8 @@ def dt_geometric_factors(
175235
176236
:arg dd: a :class:`~grudge.dof_desc.DOFDesc`, or a value convertible to one.
177237
Defaults to the base volume discretization if not provided.
178-
:returns: a :class:`~meshmode.dof_array.DOFArray` containing the geometric
179-
factors for each cell and at each nodal location.
238+
:returns: a frozen :class:`~meshmode.dof_array.DOFArray` containing the
239+
geometric factors for each cell and at each nodal location.
180240
"""
181241
from meshmode.discretization.poly_element import SimplexElementGroupBase
182242

@@ -192,14 +252,20 @@ def dt_geometric_factors(
192252
"Geometric factors are only implemented for simplex element groups"
193253
)
194254

255+
if volm_discr.dim != volm_discr.ambient_dim:
256+
from warnings import warn
257+
warn("The geometric factor for the characteristic length scale in "
258+
"time step estimation is not necessarily valid for non-volume-"
259+
"filling discretizations. Continuing anyway.", stacklevel=3)
260+
195261
cell_vols = abs(
196262
op.elementwise_integral(
197263
dcoll, dd, volm_discr.zeros(actx) + 1.0
198264
)
199265
)
200266

201267
if dcoll.dim == 1:
202-
return cell_vols
268+
return freeze(cell_vols)
203269

204270
dd_face = DOFDesc("all_faces", dd.discretization_tag)
205271
face_discr = dcoll.discr_from_dd(dd_face)
@@ -230,7 +296,7 @@ def dt_geometric_factors(
230296
)
231297
)
232298

233-
return DOFArray(
299+
return freeze(DOFArray(
234300
actx,
235301
data=tuple(
236302
actx.einsum("e,ei->ei",
@@ -240,7 +306,7 @@ def dt_geometric_factors(
240306

241307
for cv_i, sae_i in zip(cell_vols, surface_areas)
242308
)
243-
)
309+
))
244310

245311
# }}}
246312

0 commit comments

Comments
 (0)