Skip to content

feat: add transplanted 1D quadrature maps, docs, tests#159

Open
xywei wants to merge 11 commits intoinducer:mainfrom
xywei:transplanted-rules
Open

feat: add transplanted 1D quadrature maps, docs, tests#159
xywei wants to merge 11 commits intoinducer:mainfrom
xywei:transplanted-rules

Conversation

@xywei
Copy link
Contributor

@xywei xywei commented Feb 24, 2026

Summary

This PR adds transplanted 1D quadrature support to modepy using Hale–Trefethen/Kosloff–Tal-Ezer style maps, with docs and tests.

  • Add new module: modepy/quadrature/transplanted.py
    • map functions:
      • map_identity
      • map_sausage
      • map_kosloff_tal_ezer (kte / kosloff_tal_ezer)
      • map_strip
    • dispatcher:
      • map_trefethen_transplant
    • quadrature wrappers:
      • transplanted_1d_quadrature
      • transplanted_legendre_gauss_quadrature
  • Export wrappers at top level from modepy.__init__.
  • Add dedicated transplanted quadrature docs in doc/quadrature.rst with map-specific guidance and references.
  • Add transplanted-focused tests in modepy/test/test_quadrature.py (map consistency, strip endpoint rejection, KTE validation, sausage degree checks).

What for

These quadrature rules are good at integrating QBX coefficients:

image

API Notes

  • Canonical sausage API is now:
    • map_name="sausage" with sausage_degree=<odd int>
  • Backward-compatible map-name alias is still accepted in dispatcher:
    • map_name="sausage_d{odd}"
  • Public API for this feature is function/wrapper-based (no Transplanted* class API introduced).
  • exact_to is preserved for identity-preserving mappings (identity and sausage degree 1), and unset otherwise.

Documentation Updates

  • Added a dedicated transplanted section anchor in docs.
  • Clarified transformed-weight notation (w_i^{(s)} vs mapped weights).
  • Improved citation formatting (italic journal names + clickable DOI links).
  • Reduced duplicated reference text between module docstrings and doc/quadrature.rst.

Validation

  • ruff check
  • basedpyright
  • pytest modepy/test/test_quadrature.py -k transplanted
  • Full CI on PR (including docs and downstream jobs)

@inducer
Copy link
Owner

inducer commented Feb 24, 2026

Thanks! Could you look at those linter failures? The biggest one is probably adding types.

@inducer
Copy link
Owner

inducer commented Feb 24, 2026

(I can help if there's an issue.)

@inducer
Copy link
Owner

inducer commented Feb 24, 2026

cc @ShawnL00

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds transplanted 1D quadrature rules/maps (Hale–Trefethen and Kosloff–Tal-Ezer) to modepy, with accompanying docs, tests, and a QBX comparison example.

Changes:

  • Introduces modepy.quadrature.transplanted with transplant maps, a dispatcher, and transplanted quadrature classes.
  • Exports transplanted quadrature classes through package init modules (modepy.quadrature and top-level modepy).
  • Adds docs + tests for map dispatch/validation and includes a 2D QBX comparison example script.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
modepy/quadrature/transplanted.py New transplanted-map implementations and transplanted quadrature classes.
modepy/quadrature/__init__.py Re-exports transplanted quadrature classes from the quadrature package.
modepy/__init__.py Exposes transplanted quadrature classes at the top-level modepy API.
modepy/test/test_quadrature.py Adds unit tests for transplanted quadrature mapping/validation behavior.
examples/plot-qbx-transplanted-vs-gauss.py New QBX comparison example for Gauss vs transplanted rules.
doc/quadrature.rst Adds documentation section describing transplanted quadrature usage and references.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +43 to +44
OUT = "/tmp/qbx-transplanted-vs-gauss-2d.png"

Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example writes output to a hard-coded /tmp/... path, which will fail on non-POSIX platforms (e.g. Windows) and can be surprising for users.

Consider using tempfile.gettempdir()/pathlib.Path, saving relative to the current working directory, or making the output path a CLI argument.

Copilot uses AI. Check for mistakes.
Comment on lines +421 to +434
mapped_nodes, jacobian = map_trefethen_transplant(
nodes_1d,
map_name=map_name,
strip_rho=strip_rho,
kte_rho=kte_rho,
kte_alpha=kte_alpha,
)
mapped_weights = quadrature.weights * jacobian

if force_dim_axis:
mapped_nodes = np.reshape(mapped_nodes, (1, mapped_nodes.shape[0]))

super().__init__(mapped_nodes, mapped_weights)

Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Transplanted1DQuadrature drops the base rule's exact_to information even when map_name="identity" (and similarly for identity-equivalent maps like sausage_d1). That makes q.exact_to raise AttributeError despite the transplanted rule being identical to the base rule in that case.

Consider preserving exact_to when the map is exactly the identity, while leaving it unset for non-identity maps where polynomial exactness in x is generally not preserved.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@alexfikl alexfikl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nifty! 😁

I took a quick look and left some nitpicks and questions.

:members:
:show-inheritance:

Transplanted quadrature in one dimension
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel particularly strongly about this, but I haven't heard the term "transplanted" used very often, so it sounds a bit unintuitive to me. Maybe call this mapped or conformal mapped? Maybe just transformed? Although we already have a Transformed1DQuadrature that just does linear transformations.

For context, I'm mostly familiar with stuff like this from Kress' Numerical Analysis, e.g. Chapter 9.6 and some of the cited references, so it might just be a different tradition.

(Same for other places where the term is used, depending on what's decided.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — good point. I kept “transplanted” in this PR to stay consistent with Hale & Trefethen. From what I can tell, its usage mostly appear in complex analysis literature.

Copy link
Collaborator

@alexfikl alexfikl Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair enough. I'm fine with it if you guys are, just thought of raising the point for those of us less in the weeds of it 😁

One more proposal: modepy.quadrature.variable_transformation or just modepy.quadrature.transforms.

@@ -0,0 +1,292 @@
from __future__ import annotations
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example is pretty awesome, but I'm wondering if it's too big for modepy? Maybe better to just put it in pytential once this gets in?

For modepy, I would appreciate just a little example from the paper, e.g. Figure 7.1 or similar (doesn't need to be all those functions).

Comment on lines +365 to +366
if map_name == "strip":
return map_strip(s, rho=strip_rho)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't imagine we'll be calling these in a hot loop, so the order probably doesn't matter, but would it make sense to put the strip first? My understanding is that it's the latest and the greatest, so likely to be the most used in practice.

)


class Transplanted1DQuadrature(Quadrature):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be a separate quadrature class? I would expect map_trefethen_transplant (or a small wrapper) to just take a Quadrature and return a Quadrature with custom nodes + weights?

I don't feel particularly strongly about that though, since it's mostly all the same. I guess if we have Transformed1DQuadrature, this makes sense too.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that you already modified this, haha. I was hoping for a bit of discussion before moving things around 😟

@xywei xywei changed the title feat: add transplanted 1D quadrature maps, docs, tests, and QBX example feat: add transplanted 1D quadrature maps, docs, tests Feb 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants