From 8bfc41ed91590d58c1c71a6a8bdce47ca7708982 Mon Sep 17 00:00:00 2001 From: mgreminger Date: Fri, 4 Oct 2024 22:59:12 -0500 Subject: [PATCH 1/2] fix: fix derivative dimensional analysis bug Fixes case where derivative evaluates to a number but variable of integration is not defined. --- public/dimensional_analysis.py | 23 ++++++++++++++--------- tests/test_calc.spec.mjs | 12 ++++++++++++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/public/dimensional_analysis.py b/public/dimensional_analysis.py index 04d18a363..dccec022e 100644 --- a/public/dimensional_analysis.py +++ b/public/dimensional_analysis.py @@ -932,8 +932,8 @@ def ensure_dims_all_compatible(*args): if len(args) == 1: return first_arg - first_arg_dims = dimsys_SI.get_dimensional_dependencies(first_arg) - if all(dimsys_SI.get_dimensional_dependencies(arg) == first_arg_dims for arg in args[1:]): + first_arg_dims = custom_get_dimensional_dependencies(first_arg) + if all(custom_get_dimensional_dependencies(arg) == first_arg_dims for arg in args[1:]): return first_arg raise TypeError('All input arguments to function need to have compatible units') @@ -950,26 +950,26 @@ def ensure_dims_all_compatible_piecewise(*args): return ensure_dims_all_compatible(*[arg[0] for arg in args]) def ensure_unitless_in_angle_out(arg): - if dimsys_SI.get_dimensional_dependencies(arg) == {}: + if custom_get_dimensional_dependencies(arg) == {}: return angle else: raise TypeError('Unitless input argument required for function') def ensure_unitless_in(arg): - if dimsys_SI.get_dimensional_dependencies(arg) == {}: + if custom_get_dimensional_dependencies(arg) == {}: return arg else: raise TypeError('Unitless input argument required for function') def ensure_any_unit_in_angle_out(arg): # ensure input arg units make sense (will raise if inconsistent) - dimsys_SI.get_dimensional_dependencies(arg) + custom_get_dimensional_dependencies(arg) return angle def ensure_any_unit_in_same_out(arg): # ensure input arg units make sense (will raise if inconsistent) - dimsys_SI.get_dimensional_dependencies(arg) + custom_get_dimensional_dependencies(arg) return arg @@ -983,7 +983,7 @@ def ensure_inverse_dims(arg): row = [] rows.append(row) for j in range(arg.cols): - dim, _ = get_mathjs_units(cast(dict[Dimension, float], dimsys_SI.get_dimensional_dependencies(arg[j,i]))) + dim, _ = get_mathjs_units(cast(dict[Dimension, float], custom_get_dimensional_dependencies(cast(Expr, arg[j,i])))) if dim == "": row.append(sympify('0')) else: @@ -1638,6 +1638,11 @@ def get_dimensional_analysis_expression(parameter_subs: dict[Symbol, Expr], return final_expression, error +def custom_get_dimensional_dependencies(expression: Expr | None): + if expression is not None: + expression = subs_wrapper(expression, {cast(Symbol, symbol): sympify('1') for symbol in expression.free_symbols}) + return dimsys_SI.get_dimensional_dependencies(expression) + def dimensional_analysis(dimensional_analysis_expression: Expr | None, dim_sub_error: Exception | None, custom_base_units: CustomBaseUnits | None = None): custom_units_defined = False @@ -1649,13 +1654,13 @@ def dimensional_analysis(dimensional_analysis_expression: Expr | None, dim_sub_e raise dim_sub_error # Finally, evaluate dimensions for complete expression result, result_latex = get_mathjs_units( - cast(dict[Dimension, float], dimsys_SI.get_dimensional_dependencies(dimensional_analysis_expression),), + cast(dict[Dimension, float], custom_get_dimensional_dependencies(dimensional_analysis_expression),), None ) if custom_base_units is not None: custom_units, custom_units_latex = get_mathjs_units( - cast(dict[Dimension, float], dimsys_SI.get_dimensional_dependencies(dimensional_analysis_expression),), + cast(dict[Dimension, float], custom_get_dimensional_dependencies(dimensional_analysis_expression),), custom_base_units ) diff --git a/tests/test_calc.spec.mjs b/tests/test_calc.spec.mjs index e8e4b8ce7..ef15c84f2 100644 --- a/tests/test_calc.spec.mjs +++ b/tests/test_calc.spec.mjs @@ -413,3 +413,15 @@ test('Test numerical integral and derivative using interpolation function', asyn content = await page.textContent('#result-units-1'); expect(content).toBe(''); }); + +test('Test derivative dimensional analysis bug', async () => { + await page.setLatex(0, String.raw`\frac{\mathrm{d}}{\mathrm{d}\left(x\right)}\left(x+1\right)=`); + + await page.waitForSelector('.status-footer', {state: 'detached'}); + + let content = await page.textContent('#result-value-0'); + expect(parseLatexFloat(content)).toBeCloseTo(1, precision); + content = await page.textContent('#result-units-0'); + expect(content).toBe(''); +}); + From 83579fbcc74d447298811235203210f50348fa89 Mon Sep 17 00:00:00 2001 From: mgreminger Date: Fri, 4 Oct 2024 23:35:59 -0500 Subject: [PATCH 2/2] fix: fix unintentional canceling of dimensions Fixes a bug introduced in previous commit --- public/dimensional_analysis.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/dimensional_analysis.py b/public/dimensional_analysis.py index dccec022e..fe0e5666b 100644 --- a/public/dimensional_analysis.py +++ b/public/dimensional_analysis.py @@ -90,6 +90,10 @@ class ExprWithAssumptions(Expr): information, ) +dimensions = (mass, length, time, current, temperature, luminous_intensity, + amount_of_substance, angle, information) +dimension_symbols = set((dimension.name for dimension in dimensions)) + from sympy.physics.units.systems.si import dimsys_SI from sympy.utilities.iterables import topological_sort @@ -1640,7 +1644,7 @@ def get_dimensional_analysis_expression(parameter_subs: dict[Symbol, Expr], def custom_get_dimensional_dependencies(expression: Expr | None): if expression is not None: - expression = subs_wrapper(expression, {cast(Symbol, symbol): sympify('1') for symbol in expression.free_symbols}) + expression = subs_wrapper(expression, {cast(Symbol, symbol): sympify('1') for symbol in (expression.free_symbols - dimension_symbols)}) return dimsys_SI.get_dimensional_dependencies(expression) def dimensional_analysis(dimensional_analysis_expression: Expr | None, dim_sub_error: Exception | None,