-
Notifications
You must be signed in to change notification settings - Fork 28
add radian to strain transform #521
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds a new radians_to_strain transform with unit validation and conversion, re-exports it from dascore.transform, adds tests and a bibliography entry, and introduces a small local alias in Patch.channel_count. No existing public function signatures were removed. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant Patch as Patch
participant Transform as radians_to_strain
participant Units as Unit Utils
User->>Patch: call Patch.radians_to_strain(gauge_length?)
Patch->>Transform: forward Patch and parameters
Transform->>Patch: read data_units and attrs.gauge_length
alt no "rad" in data_units
Transform->>User: warn and return original Patch
else
Transform->>Transform: validate gauge_length (error if missing)
Transform->>Units: get_factor_and_unit(patch.data_units)
Units-->>Transform: factor, unit
Transform->>Transform: compute const = wave_length / (4π * n * gauge * stress_const)
Transform->>Patch: new_data = data * const * factor
Transform->>Units: compute new_units (remove "rad" → strain)
Transform->>User: return Patch with updated data and data_units
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #521 +/- ##
=======================================
Coverage 99.87% 99.87%
=======================================
Files 121 121
Lines 9887 9912 +25
=======================================
+ Hits 9875 9900 +25
Misses 12 12
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (6)
docs/references.bib (1)
50-59: Nice addition; consider adding DOI/URL metadata.Adding a DOI (and optionally the URL) improves reproducibility and doc builds that auto-link citations.
dascore/core/patch.py (1)
256-256: Group transform aliases with the other transform methods.For consistency/readability, place this alongside the other transform method mappings (lines 346+). Also prefer
transform.radians_to_strainto match nearby aliases.Apply this diff:
- radians_to_strain = dascore.transform.radians_to_strainAdd near the other transform mappings:
@@ tau_p = transform.tau_p + radians_to_strain = transform.radians_to_straintests/test_transform/test_strain.py (1)
195-197: Guard against divide-by-zero in ratio check.Random data may contain zeros; mask to avoid spurious inf/NaN.
Apply this diff (in addition to the above):
- const = out.data / rad_patch.data - assert np.allclose(const, expected_const) + mask = rad_patch.data != 0 + const = out.data[mask] / rad_patch.data[mask] + assert np.allclose(const, expected_const)dascore/transform/__init__.py (1)
13-16: Fix linter issues: wrap long import and declare re-exports.Addresses E501 and F401 by line-wrapping and making the re-exports explicit.
Apply this diff:
-from .strain import velocity_to_strain_rate, velocity_to_strain_rate_edgeless, radians_to_strain +from .strain import ( + velocity_to_strain_rate, + velocity_to_strain_rate_edgeless, + radians_to_strain, +) + +# Re-exported API +__all__ = [ + "differentiate", + "rfft", + "dft", + "idft", + "integrate", + "spectrogram", + "dispersion_phase_shift", + "tau_p", + "velocity_to_strain_rate", + "velocity_to_strain_rate_edgeless", + "radians_to_strain", +]dascore/transform/strain.py (2)
214-218: Docstring: clarify “strain (rate)” → “strain”.Equation 3 converts phase (radians) to strain, not strain rate.
Apply this diff:
- Convert data in radians to strain (rate). + Convert data in radians to strain.
260-263: Numerical constant matches Eq. 3; consider explicit typing for gauge_length.Optional: annotate
gauge_length: float | Nonefor clarity.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
dascore/core/patch.py(1 hunks)dascore/transform/__init__.py(1 hunks)dascore/transform/strain.py(2 hunks)docs/references.bib(1 hunks)tests/test_transform/test_strain.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
dascore/transform/__init__.py (1)
dascore/transform/strain.py (1)
radians_to_strain(206-268)
dascore/core/patch.py (1)
dascore/transform/strain.py (1)
radians_to_strain(206-268)
tests/test_transform/test_strain.py (6)
tests/conftest.py (2)
random_patch(282-284)patch(375-377)dascore/core/patch.py (3)
data(213-215)attrs(203-205)Patch(27-380)dascore/transform/strain.py (1)
radians_to_strain(206-268)dascore/units.py (1)
get_unit(47-58)dascore/proc/basic.py (1)
update_attrs(82-108)dascore/exceptions.py (1)
ParameterError(26-27)
dascore/transform/strain.py (5)
dascore/exceptions.py (1)
ParameterError(26-27)dascore/units.py (2)
get_factor_and_unit(97-106)get_unit(47-58)dascore/utils/patch.py (1)
patch_function(179-285)dascore/core/attrs.py (2)
get(174-179)update(234-248)dascore/proc/basic.py (1)
update(152-192)
🪛 Ruff (0.12.2)
dascore/transform/__init__.py
13-13: .strain.velocity_to_strain_rate imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
13-13: .strain.velocity_to_strain_rate_edgeless imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
13-13: .strain.radians_to_strain imported but unused; consider removing, adding to __all__, or using a redundant alias
(F401)
13-13: Line too long (96 > 88)
(E501)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
- GitHub Check: test_build_docs
- GitHub Check: test_code (windows-latest, 3.12)
- GitHub Check: test_code (macos-latest, 3.12)
- GitHub Check: test_code (windows-latest, 3.10)
- GitHub Check: test_code_min_deps (windows-latest, 3.12)
- GitHub Check: test_code_min_deps (macos-latest, 3.13)
- GitHub Check: test_code (macos-latest, 3.11)
- GitHub Check: test_code (windows-latest, 3.11)
- GitHub Check: test_code (ubuntu-latest, 3.10)
- GitHub Check: test_code_min_deps (ubuntu-latest, 3.12)
- GitHub Check: test_code (ubuntu-latest, 3.12)
- GitHub Check: test_code (macos-latest, 3.10)
- GitHub Check: test_code_min_deps (macos-latest, 3.12)
- GitHub Check: test_code_min_deps (windows-latest, 3.13)
- GitHub Check: test_code (ubuntu-latest, 3.11)
- GitHub Check: test_code_min_deps (ubuntu-latest, 3.13)
🔇 Additional comments (1)
dascore/transform/strain.py (1)
7-8: Imports look correct.NumPy and unit helpers are required by the new function.
Also applies to: 13-14
| def test_convert_data_units(self, rad_patch): | ||
| """Ensure we can convert data units to strain.""" | ||
| out = rad_patch.radians_to_strain() | ||
| # Strain should now be in the units. | ||
| assert str(dc.get_unit("strain")) in str(out.attrs.data_units) | ||
| # Ensure the constant published in @lindsey2020broadband eq 3 works | ||
| # because the test data also have 10m gauge length. | ||
| expected_const = 11.6e-9 | ||
| const = out.data / rad_patch.data | ||
| assert np.allclose(const, expected_const) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid magic number; compute expected constant from the formula.
Hard-coding 11.6e-9 is brittle and appears inconsistent with the defaults used in the implementation.
Apply this diff:
- # Ensure the constant published in @lindsey2020broadband eq 3 works
- # because the test data also have 10m gauge length.
- expected_const = 11.6e-9
- const = out.data / rad_patch.data
- assert np.allclose(const, expected_const)
+ # Compute the expected constant from Eq. 3 using the defaults.
+ expected_const = (1550.0e-9) / (4 * np.pi * 1.445 * rad_patch.attrs.gauge_length * 0.79)
+ const = out.data / rad_patch.data
+ assert np.allclose(const, expected_const)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def test_convert_data_units(self, rad_patch): | |
| """Ensure we can convert data units to strain.""" | |
| out = rad_patch.radians_to_strain() | |
| # Strain should now be in the units. | |
| assert str(dc.get_unit("strain")) in str(out.attrs.data_units) | |
| # Ensure the constant published in @lindsey2020broadband eq 3 works | |
| # because the test data also have 10m gauge length. | |
| expected_const = 11.6e-9 | |
| const = out.data / rad_patch.data | |
| assert np.allclose(const, expected_const) | |
| def test_convert_data_units(self, rad_patch): | |
| """Ensure we can convert data units to strain.""" | |
| out = rad_patch.radians_to_strain() | |
| # Strain should now be in the units. | |
| assert str(dc.get_unit("strain")) in str(out.attrs.data_units) | |
| # Compute the expected constant from Eq. 3 using the defaults. | |
| expected_const = (1550.0e-9) / (4 * np.pi * 1.445 * rad_patch.attrs.gauge_length * 0.79) | |
| const = out.data / rad_patch.data | |
| assert np.allclose(const, expected_const) |
🤖 Prompt for AI Agents
In tests/test_transform/test_strain.py around lines 188 to 197, the test uses a
magic number 11.6e-9 for expected_const; replace that hard-coded value by
computing expected_const from the same formula used by radians_to_strain
(Lindsey 2020 broadband eq. 3) using the patch's gauge length (rad_patch.attrs
or rad_patch.gauge_length, default 10 m) and proper unit conversions via dc
utilities so the test derives the expected constant dynamically and then asserts
np.allclose(const, expected_const).
|
✅ Documentation built: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (2)
tests/test_transform/test_strain.py (1)
188-197: Replace magic number with formula-derived constant (Eq. 3).Hard-coding 11.6e-9 is brittle; compute from the same defaults the implementation uses.
- # Ensure the constant published in @lindsey2020broadband eq 3 works - # because the test data also have 10m gauge length. - expected_const = 11.6e-9 - const = out.data / rad_patch.data - assert np.allclose(const, expected_const) + # Compute the expected constant from Eq. 3 using defaults and GL=10 m. + wl = 1550.0e-9 + n = 1.445 + zeta = 0.79 + Lg = rad_patch.attrs.gauge_length + expected_const = wl / (4 * np.pi * n * Lg * zeta) + const = out.data / rad_patch.data + assert np.allclose(const, expected_const)dascore/transform/strain.py (1)
246-254: Radian presence check is brittle and may miss aliases; also fails on non-str inputs.Prefer a lowercase substring check on the original units string; accept “rad”, “radian(s)”.
- # If units doesn't contain radians just return so function is idempotent - quant = dc.get_quantity(patch.attrs.data_units) - if str(dc.get_unit("radians")) not in str(quant): + # If units don't contain any radian component, return idempotently. + units_str = str(patch.attrs.get("data_units", "")).lower() + if "rad" not in units_str: msg = ( f"Patch {patch} has no radians in its data_units, " f"skipping strain conversion." ) warnings.warn(msg) return patch
🧹 Nitpick comments (5)
tests/test_transform/test_strain.py (3)
175-187: Fixture is fine; consider modern RNG API.Use numpy’s Generator for forward-compatibility and reproducibility.
- rand = np.random.RandomState(42) - data = rand.random(random_patch.data.shape) - 0.5 + rng = np.random.default_rng(42) + data = rng.random(random_patch.data.shape) - 0.5
199-208: Strengthen positive path check.After the ParameterError case, also verify scaling is correct for an explicit gauge_length.
out = patch.radians_to_strain(gauge_length=1) assert isinstance(out, dc.Patch) + wl, n, zeta = 1550.0e-9, 1.445, 0.79 + expected = wl / (4 * np.pi * n * 1 * zeta) + assert np.allclose(out.data / rad_patch.data, expected)
209-217: Idempotence assertion is good; optionally also assert no history appended.Ensures zero-op transform truly leaves Patch untouched.
assert out is patch + assert list(getattr(out.attrs, "history", ())) == list( + getattr(patch.attrs, "history", ()) + )dascore/transform/strain.py (2)
214-215: Doc: this converts to strain, not strain-rate.Fix the wording.
- Convert data in radians to strain (rate). + Convert data in radians to strain.
266-268: Consider setting data_type="strain" on output.Aligns attrs with the unit change.
- new_attrs = patch.attrs.update(data_units=new_units) + new_attrs = patch.attrs.update(data_units=new_units, data_type="strain")
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
dascore/transform/strain.py(2 hunks)tests/test_transform/test_strain.py(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
dascore/transform/strain.py (3)
dascore/exceptions.py (1)
UnitError(106-107)dascore/units.py (4)
convert_units(120-155)get_factor_and_unit(97-106)get_unit(47-58)get_quantity(70-94)dascore/utils/patch.py (1)
patch_function(179-285)
tests/test_transform/test_strain.py (4)
dascore/exceptions.py (2)
ParameterError(26-27)UnitError(106-107)dascore/core/patch.py (3)
data(213-215)attrs(203-205)Patch(27-380)dascore/transform/strain.py (1)
radians_to_strain(206-268)dascore/units.py (1)
get_unit(47-58)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
- GitHub Check: test_code_min_deps (windows-latest, 3.13)
- GitHub Check: test_code_min_deps (windows-latest, 3.12)
- GitHub Check: test_code_min_deps (macos-latest, 3.13)
- GitHub Check: test_code_min_deps (ubuntu-latest, 3.12)
- GitHub Check: test_code_min_deps (ubuntu-latest, 3.13)
- GitHub Check: test_code_min_deps (macos-latest, 3.12)
- GitHub Check: test_code (windows-latest, 3.11)
- GitHub Check: test_code (windows-latest, 3.12)
- GitHub Check: test_code (windows-latest, 3.10)
- GitHub Check: test_code (macos-latest, 3.11)
- GitHub Check: test_code (ubuntu-latest, 3.12)
- GitHub Check: test_code (macos-latest, 3.12)
- GitHub Check: test_code (macos-latest, 3.10)
- GitHub Check: test_code (ubuntu-latest, 3.10)
- GitHub Check: test_code (ubuntu-latest, 3.11)
- GitHub Check: test_build_docs
🔇 Additional comments (1)
tests/test_transform/test_strain.py (1)
218-222: Good negative test for complex units.Covers the “rad^2” path and expected UnitError.
| gauge_length ($L_g$) | ||
| The gauge length in meters. | ||
| wave_length ($\lambda$) | ||
| The laser wavelength in m. | ||
| stress_constant ($\zeta$) | ||
| The stress constant. | ||
| refractive_index ($\Delta \Phi$) | ||
| The refractive index of the cable. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doc: incorrect symbol/description for refractive index.
Use n for refractive index; ΔΦ is the phase change (the input), not n.
- refractive_index ($\Delta \Phi$)
- The refractive index of the cable.
+ refractive_index ($n$)
+ The refractive index of the fiber.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| gauge_length ($L_g$) | |
| The gauge length in meters. | |
| wave_length ($\lambda$) | |
| The laser wavelength in m. | |
| stress_constant ($\zeta$) | |
| The stress constant. | |
| refractive_index ($\Delta \Phi$) | |
| The refractive index of the cable. | |
| gauge_length ($L_g$) | |
| The gauge length in meters. | |
| wave_length ($\lambda$) | |
| The laser wavelength in m. | |
| stress_constant ($\zeta$) | |
| The stress constant. | |
| - refractive_index ($\Delta \Phi$) | |
| refractive_index ($n$) | |
| The refractive index of the fiber. |
🤖 Prompt for AI Agents
In dascore/transform/strain.py around lines 221 to 229, the docstring
incorrectly labels the refractive index as "ΔΦ" and describes it as a phase
change; update the parameter name and description to use n for the refractive
index and clarify that ΔΦ is the phase change input (if present elsewhere), e.g.
change the parameter entry to "refractive_index (n) The refractive index of the
cable." and, if the phase change ΔΦ is documented here, add a separate entry
"phase_change (ΔΦ) The measured phase change." Ensure symbols and descriptions
match the function's inputs and any formulas in the docstring.
| data_units = patch.attrs.get("data_units", None) | ||
| d_factor, d_units = get_factor_and_unit(data_units, simplify=True) | ||
| new_units = get_unit(d_units) * get_unit("strain/radians") | ||
| # Radians wasn't eliminated from the output units. Something went wrong. | ||
| if str(dc.get_unit("radians")) in str(new_units): | ||
| msg = f"radians to strain failed to convert {data_units} to strain." | ||
| raise UnitError(msg) | ||
| # Build output patch | ||
| new_attrs = patch.attrs.update(data_units=new_units) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
new_units construction can raise on d_units=None and uses non-canonical “radians”.
Handle empty units safely and use singular “radian”; explicitly fail if rad remains.
- data_units = patch.attrs.get("data_units", None)
- d_factor, d_units = get_factor_and_unit(data_units, simplify=True)
- new_units = get_unit(d_units) * get_unit("strain/radians")
- # Radians wasn't eliminated from the output units. Something went wrong.
- if str(dc.get_unit("radians")) in str(new_units):
- msg = f"radians to strain failed to convert {data_units} to strain."
- raise UnitError(msg)
- # Build output patch
- new_attrs = patch.attrs.update(data_units=new_units)
+ data_units = patch.attrs.get("data_units", None)
+ d_factor, d_units = get_factor_and_unit(data_units, simplify=True)
+ if d_units:
+ new_units = get_unit(d_units) * (get_unit("strain") / get_unit("radian"))
+ else:
+ new_units = get_unit("strain")
+ # If radians remain, units were too complex; bail out.
+ if "rad" in str(new_units).lower():
+ msg = f"radians to strain failed to convert {data_units} to strain."
+ raise UnitError(msg)
+ # Build output patch
+ new_attrs = patch.attrs.update(data_units=new_units)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| data_units = patch.attrs.get("data_units", None) | |
| d_factor, d_units = get_factor_and_unit(data_units, simplify=True) | |
| new_units = get_unit(d_units) * get_unit("strain/radians") | |
| # Radians wasn't eliminated from the output units. Something went wrong. | |
| if str(dc.get_unit("radians")) in str(new_units): | |
| msg = f"radians to strain failed to convert {data_units} to strain." | |
| raise UnitError(msg) | |
| # Build output patch | |
| new_attrs = patch.attrs.update(data_units=new_units) | |
| data_units = patch.attrs.get("data_units", None) | |
| d_factor, d_units = get_factor_and_unit(data_units, simplify=True) | |
| if d_units: | |
| new_units = get_unit(d_units) * (get_unit("strain") / get_unit("radian")) | |
| else: | |
| new_units = get_unit("strain") | |
| # If radians remain, units were too complex; bail out. | |
| if "rad" in str(new_units).lower(): | |
| msg = f"radians to strain failed to convert {data_units} to strain." | |
| raise UnitError(msg) | |
| # Build output patch | |
| new_attrs = patch.attrs.update(data_units=new_units) |
🤖 Prompt for AI Agents
In dascore/transform/strain.py around lines 258 to 266, new_units construction
can crash if d_units is None and the code checks/uses the non-canonical string
"radians"; also patch.attrs.update(...) returns None. Fix by first handling
d_units is None (treat as dimensionless) before calling
get_unit/get_factor_and_unit, use the singular "radian" when multiplying units,
then explicitly detect remaining "radian" in the resulting unit and raise
UnitError if present, and finally build new_attrs by copying patch.attrs (e.g.,
attrs_copy = patch.attrs.copy()) and then updating
attrs_copy['data_units']=new_units instead of assigning the return value of
update().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
data units cant be `None because we check radians is in units in the line above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@d-chambers You're absolutely right! Looking at the control flow, the function already checks for radians in patch.attrs.data_units and returns early if none are found, so by the time we reach the unit conversion logic, data_units will indeed contain radians and won't be None. My suggestion to handle the None case was unnecessary given this guard. Thank you for the clarification!
Description
Adds a helper function for converting from radians to strain using Equation 3 of @lindsey2020broadband.
Checklist
I have (if applicable):
Summary by CodeRabbit