Repository navigation
Conversation
The docstrings were converted mechanically from Doxygen output, with C++ types and a layout that no documentation tool parses. They are now NumPy-style docstrings with Python types and argument names, and the roughly 150 functions and methods that had none now have one. Each overloaded function has a single docstring describing all of its forms, attached to its last overload, and unnamed arguments now have names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The functions defined in Python, in the package itself, in the plotting module, and in the extrapolation module, now have NumPy-style docstrings like those of the compiled module, so that they render alike in the documentation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RunExtrapolation still called the Python 2 builtin `file`, so it raised a NameError on Python 3 before running anything. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The documentation is built with Sphinx, the Furo theme, and MyST-NB. The API reference is generated from the docstrings, with a small extension that gives each pybind11 overload its own signature. The old IPython notebook that accompanied the paper is ported to a MyST notebook for current GWFrames and Python 3, which is executed on every build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Docs workflow builds the documentation on every push and pull request, treating warnings as errors, and deploys it to GitHub Pages from main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A badge and a link near the top of the README point to the documentation, the usage section points to the tutorial and API reference, and the development section explains how to build it. The installation and change sections are marked so that the documentation can include them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DominantPrincipalValue freed its eigen-system workspace inside the loop over matrices, so every matrix after the first was diagonalized with freed memory, and the workspace was freed again on each pass; with two or more matrices the process aborted. The workspace is now freed once, after the loop, as in SubordinatePrincipalValue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Matrix::clear freed its GSL matrix without forgetting it, so the destructor freed it a second time, and every query of an empty Matrix dereferenced a null pointer. Now clear resets the pointer, an empty Matrix (with zero rows or zero columns) is always represented by a null pointer and reports zero rows and zero columns, and the constructors, assignments, and resize allocate nothing for an empty shape. Copy assignment also uses copy-and-swap, so self-assignment no longer reads freed memory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The loops over m that load the mode data of the two factors into the work arrays ran only from m = -ell to m = -ell, so each factor was represented by one coefficient per ell, stored at the wrong indices. The loops now run over every m from -ell to ell, as the rest of the function assumes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pointwise quotient shared its code with the product, which set the spin weight of the result to the sum of the spin weights of the inputs. For a quotient that is wrong, and the wrong spin weight also made the transform back to modes produce wrong data. The quotient now has the difference of the spin weights, and the Waveform docstring describes the spin weight, the modes, and the accuracy of the result of both operators. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new tests multiply and divide by constant waveforms, compare products of spin-weighted fields with the products of their values at points on the sphere, and check that a quotient undoes a product. They also check the spin weight and the modes of each result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With AllowTimesOutsideCurrentDomain, the splines were evaluated at the wrong entries of the times inside the domain whenever some new times preceded the data, and the searches for the domain boundaries could read past the end of NewTime when every new time was outside it. The index is now offset, the searches are bounded, the frame outside the domain is held at the first or last rotor of the original frame, and the boost weight and version history are now copied to the result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The upper bound of the coarse search over time shifts in AlignWaveforms was computed from the number of time steps of B, as if it were a time. When the times of B were large compared to the number of steps, the search window was empty and the alignment failed, and in other cases the window could extend past the end of the data of B. The bound now uses the second-to-last time of B, matching the margin of the lower bound, and the docstring describes the search range. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Waveform.Compare called Intersection with its default arguments, so the MinTimeStep and MinTime arguments of Compare were silently ignored. They are now passed on to Intersection, and the docstring describes them as working parameters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DropTimesOutside used huntRight to find the first time to keep, so it always dropped the first time step and any time equal to ta, kept the first time step when tb preceded all the data, and corrupted memory when tb was less than ta. The bounds are now found with std::lower_bound and std::upper_bound, so exactly the times with ta <= t <= tb remain, and an empty interval removes every time step. The docstring and new regression tests describe the corrected behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Waveform::FindModeIndexWithoutError read lm[i_expected] without checking that the index was in range, and NormalizedAntisymmetry and BinaryOp then indexed the data with the invalid index it returns for a missing mode, which crashed or read garbage. The lookup now checks the bound first, as FindModeIndex does, and its callers treat a missing mode, such as the (ell,-m) partner of a mode that is present, as zero. The NormalizedAntisymmetry docstring now says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a Waveform had no frame rotors, as any Waveform built directly from times, modes, and data does, Frame(TimeIndex) read the first element of the empty frame vector, which crashed Python. An empty frame is the identity, so the identity rotor is now returned, and the docstring says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SliceOfTimeIndicesWithoutModes copied frame data only when there was one rotor per time step, so a constant (single-rotor) frame was silently dropped, and it also skipped the index checks of the other slicing functions. It now treats frames exactly as SliceOfTimeIndices and SliceOfTimeIndicesWithEll2 do: it slices a time-dependent frame, copies a constant frame, and rejects any other frame size and any impossible pair of indices. The docstrings no longer describe the constant frame as dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Modes.bar conjugated each coefficient in place, which does not give the modes of the complex-conjugate function, so the supermomentum computed from sigma-bar was wrong. It now returns (-1)^(s+m) times the conjugate of the (ell, -m) coefficient, which agrees with conjugating the function on a grid. The docstring and the SWIG reference comparison are updated to match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The symmetric and antisymmetric parts under x, y, z, and full parity applied the same combination (R +/- P R)/2 to the frame rotors as to the modes, which gave non-unit rotors and, for the full-parity antisymmetric part, an all-zero frame. The modes are combined index by index, as in a sum of waveforms, so the result now keeps the frame of the original waveform, and the docstrings say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DataGrid::pow and Modes::pow kept the spin weight of their input, although a product of grids adds spin weights, so the p-th power of a spin-s function has spin weight p*s. For Modes this was more than a wrong label, because the powered grid was transformed back to modes with the wrong spin weight. DataGrid::pow now sets the spin weight to p*s, which Modes::pow inherits, and the docstrings say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pointwise products and quotients of waveforms truncated both inputs to the smaller of their largest ell values and used a grid that resolved only that band limit, so the higher modes of a product were aliased into the returned coefficients. Every mode of each input is now transformed, and the grid resolves the sum of the largest ell values of the inputs, so the returned modes of a product are exact; the returned set of modes is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Waveform::InterpolateInPlace wrote the waveform's whole history back into its own history stream, so each call doubled the history. It now appends only the single line "this->InterpolateInPlace(NewTime);", as the other in-place methods do. The docstring says so, and new regression tests check the history and data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dividing a number by a DataGrid of spin weight s returned a grid labeled with spin weight s, although the quotient has spin weight -s, as DataGrid.pow(-1) and grid-by-grid division already give. The quotient now has spin weight -s. The DataGrid docstring now states the spin weight of each scalar operation, and notes that sums and differences, with a number or another grid, are meaningful only for matching spin weights but are not checked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Modes::pow sized its grid as 2*EllMax()*p+1, which DataGrid enlarged to 2*EllMax()+1 for p <= 0, so negative powers, which are not band-limited, suffered aliasing errors as large as the first omitted modes. The grid now has 2*EllMax()*abs(p)+1 points in each direction, as for the power abs(p), and negative powers are still truncated at the original EllMax(). Non-negative powers, which are exact, are unchanged, and the docstring describes both cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both overloads of Modes::EvaluateAtPoint summed over every stored mode from ell = 0, so for any nonzero spin weight they asked for a Wigner D element with ell < |s| and raised an error. The sums now start at ell = |s|, skipping the modes that vanish for a spin-weighted function, which also makes the boosted DataGrid constructor usable for spin-weighted data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The caster for vectors of vectors located each row with numpy's bounds-checked `data(i, 0)`, which raises an IndexError when the rows have length zero, so `Matrix([[]])` and any argument or result with rows but no columns failed to convert. The caster now offsets the base pointer of the contiguous array by `i * cols` instead, in both directions, which leaves non-empty conversions unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spin-0 data with only the ell=0 mode were put on a 1x1 grid, which spinsfast cannot handle, so products of such Waveforms, and products and powers of such Modes, crashed. The grids for pointwise operations are now never smaller than 3x3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Matrix(DataIn) took the number of columns from the first row and read every row to that length, so a shorter row was read past its end. It now raises ValueError unless all rows have the same length. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SliceModes.MoreschiIteration was never finished: it partly overwrote one of its arguments and then always raised an error. It is removed from the C++ code, the bindings, and the documentation. SuperMomenta.MoreschiIteration, which works, is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DataGrid + DataGrid and DataGrid - DataGrid did not check that the spin weights agree, unlike the same operators on Modes; they now raise ValueError when the spin weights differ. A number can now also appear on the right of a DataGrid in all four operators, where before only a number on the left was bound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The docstrings were converted mechanically from Doxygen output, with C++
types and a layout that no documentation tool parses. They are now
NumPy-style docstrings with Python types and argument names, and the
roughly 150 functions and methods that had none now have one. Each
overloaded function has a single docstring describing all of its forms,
attached to its last overload, and unnamed arguments now have names.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com