Port FD temperature solver, actuator chain, and standalone bugs#9
Merged
Port FD temperature solver, actuator chain, and standalone bugs#9
Conversation
Restores Python parity with MATLAB across the previously-broken cantilever methods. Net effect: every public method that worked in MATLAB now works in Python (default config or appropriately configured cantilever). Finite-difference temperature solver ------------------------------------ calculateTempProfile and calculateTempProfileTempDependent had layered porting bugs that made them raise on every input: - ``np.arange(0, totalLength + 1, dx)`` produced a length far longer than ``n_points`` because ``+1`` was a misread of MATLAB's ``0:dx:totalLength`` idiom. Switched to ``np.linspace`` to mirror MATLAB exactly. - ``np.nonzero(...)`` returns a 1-tuple of arrays in NumPy; the MATLAB-style scalar indexing ``step_indices[i]`` was iterating the tuple. Switched to ``np.where(...)[0]`` for flat index arrays. - All FD arrays (K, perimeter, Q, rhs, k_x, Rsheet_x) used column- vector ``(N, 1)`` shapes inherited from MATLAB. Indexing ``K[ii-1]`` returned a 1-element array which then failed to assign into a scalar matrix slot. Rewrote the solver with 1-D arrays throughout. - The boundary row was indexed at ``A[n_points, ...]`` (out of bounds) and the adiabatic-tip RHS was ``[1 - 1]`` (parses to ``[0]``, not ``[1, -1]``). Fixed both. - The interior loop ran ``range(2, n_points)`` (Python 0-indexed), leaving row 1 unset and aliasing the MATLAB 1-indexed loop bounds. Now ``range(1, n_points - 1)``. - ``rhs[ii, 1] = ...`` wrote to column 1 of a single-column array. Rewritten as a 1-D vector. - A scalar-input call to ``Rsheet_x(index_range)`` used MATLAB function-call syntax instead of indexing. - The ``cantilever_type='none'`` case was missing entirely; added a no-op branch matching MATLAB. The cascade methods that consume the FD result (averagePRTemp, maxPRTemp, tempRiseAtPRBase, averageActuatorDeltaTemp, thermalCrosstalk, dR_with_temp_rise) were calling ``temp(indices)`` (MATLAB syntax) instead of ``temp[indices]``; all switched to 1-D indexing returning proper Python scalars. thermal_conductivity_profile and RSheetProfile applied ``np.transpose`` to 1-D arrays, which is a no-op in NumPy. Replaced with ``arr.reshape(-1, 1)`` so the per-depth doping and per-x temperature broadcast correctly into the ``(numZPoints, numXPoints)`` matrix. Also corrected ``np.mean(k_z_x, 1)`` (MATLAB collapses dim 1 = rows = numpy axis 0). Actuator stress / deflection chain ---------------------------------- calculateActuatorStress, calculateDeflection, lookupActuatorMechanics, and tipDeflection all had similar shape and indexing bugs. Rewrote with 1-D arrays: - ``film_intrinsic_stress`` now returns a flat ``(n_layers,)`` vector instead of the column-shaped ``(3, 1)`` / ``(5, 1)`` it returned before, matching the way MATLAB's auto-allocated row vector behaves when broadcast against ``ones(numXPoints, 1)``. Also adds an explicit ``cantilever_type='none'`` branch returning zeros, fixing the UnboundLocalError on ``sigma_i``. - ``calculateActuatorStress`` reshapes ``(temp - T_ref)`` to a column before broadcasting against ``(cte * E)`` so the result is the correct ``(numXPoints, n_layers)`` matrix, and adds a 'none' case returning zeros. - ``calculateDeflection`` switches to ``np.linspace`` and a 1-D curvature array; the ``range(1, x.size + 1)`` loop is now ``range(n_points)`` and the curvature denominator is computed once outside the loop. - ``lookupActuatorMechanics`` builds ``z_layers`` as a 1-D array, fixes the off-by-one in the centroid loop, and raises explicitly for ``cantilever_type='none'``. - ``tipDeflection`` short-circuits to ``0.0`` for ``'none'`` so the optimizer's TIP_DEFLECTION metric is well-defined on the default. - ``v_actuator`` is now initialized to 0 in ``Cantilever.__init__``; previously only set inside instance methods that toggled it. - ``calculateEnergies`` (used for Rayleigh-Ritz omega_vacuum on actuated cantilevers) had the same column-vector / wrong-endpoint / swapped-trapezoid-args bugs. Rewritten with 1-D arrays and correct ``np.trapezoid(y, x)`` argument order. Standalone bugs --------------- - ``d31`` mistakenly passed ``self.t_a`` as the ``kind`` argument to ``scipy.interpolate.interp1d``, causing a "tuple indices" error. Replaced with ``CubicSpline`` (the analog of MATLAB's ``interp1(..., 'spline')``) and evaluated at ``t_a``. - ``calculateEquivalentThickness`` and ``omega_vacuum`` passed ``"xtol"`` as the 4th positional argument to ``optimize.fminbound``, which is the ``args`` tuple. Now passed as a keyword. - ``film_intrinsic_stress``'s ``np.diff`` produced a 1-element array that ``float(...)`` rejected. Use ``hi - lo`` directly. - ``tip_deflection_distribution`` and ``plot_tip_deflection_distribution`` pre-allocated ``z`` with the wrong number of rows; rewritten to size from the first deflection call and use a try/finally to restore actuator state. Optimizer surface ----------------- TEMP_TIP_EXACT and TEMP_MAX_EXACT are restored to the ``CantileverMetric`` enum now that ``calculateMaxAndTipTemp`` works. The "F-D Temp Rises" line is restored in ``print_performance``. Tests (33 new) cover the FD solver, the actuator chain on both 'none' and 'thermal' configurations, all standalone bug fixes, the resonant frequency on actuated cantilevers, ``print_performance`` running end-to-end, and the optimizer accepting a ``TEMP_MAX_EXACT`` constraint.
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.
Summary
Restores Python parity with MATLAB across the cantilever methods that
were genuinely broken on master. After this PR every public method
that worked in MATLAB now works in Python: the default
cantilever_type='none'configuration runs thenoise/resolution/temperature/print pipeline end-to-end, and the
actuated configurations (
step,thermal,piezoelectric)run the deflection / actuator-stress / Rayleigh-Ritz frequency
pipeline end-to-end.
Audit before this PR: 28 no-arg methods raised on a default
cantilever, of which most were genuine port bugs (cascading shape
mismatches, MATLAB-style 1-indexed loops, function-call vs.
indexing syntax, scipy API misuse). After this PR: only the methods
that should raise on a default cantilever still raise -- those are
methods that compute properties of an actuator stack which doesn't
exist on `cantilever_type='none'` (e.g.
`lookupActuatorMechanics`, `actuatorNeutralAxis`,
`heaterTimeConstant`), or fluid properties on `fluid='vacuum'`.
Each raises with an informative message that names the configuration
required, matching MATLAB's behaviour. On a configured thermal
cantilever every public no-arg method runs cleanly.
Categories of fixes
Finite-difference temperature solver (~10 cascading methods)
`calculateTempProfile` had layered porting bugs: wrong
`np.arange` endpoint, `np.nonzero` tuple iteration, column-vector
shape that broke scalar slot assignments, out-of-bounds boundary
condition, off-by-one loop bounds, and the `[1 - 1]` literal that
parsed to `[0]` instead of `[1, -1]`. Rewrote with 1-D arrays.
`thermal_conductivity_profile` and `RSheetProfile` had
`np.transpose` applied to 1-D arrays (a no-op in NumPy); replaced
with explicit `reshape(-1, 1)`. `np.mean(k_z_x, 1)` was using a
MATLAB axis convention; corrected to axis 0.
Actuator stress / deflection chain (~6 methods)
Same family of shape and 1-indexed-loop bugs in
`calculateActuatorStress`, `calculateDeflection`,
`lookupActuatorMechanics`, `tipDeflection`,
`film_intrinsic_stress`, `calculateEnergies`. Rewrote with 1-D
arrays. Added a `cantilever_type='none'` branch to the stress
methods (returns zero stress / zero deflection, which is physically
correct -- no actuator means no actuator-driven deflection).
Initialized `v_actuator = 0` in `init` so methods that read
it don't fail when the user doesn't explicitly configure an actuator.
Standalone bugs
`t_a` in the `kind` slot. Replaced with `CubicSpline`
(matches MATLAB's `interp1(..., 'spline')`).
`optimize.fminbound` was passed `"xtol"` as the 4th
positional, which is the `args` tuple. Now keyword-only.
that `float(...)` rejected. Compute the range directly.
`plot_tip_deflection_distribution`: size-buffer-from-first-call
pattern instead of pre-allocating with the wrong row count.
Optimizer surface restorations
`CantileverMetric` enum -- they wrap
`calculateMaxAndTipTemp` which now works.
Cross-checks
`approxTempRise` agree to ~0.5% for a default PR-only cantilever.
~118 K FD-computed peak temperature vs ~9 K from the lumped model
(which only accounts for PR heating); the FD result correctly
captures the actuator power and produces a physically reasonable
~3.5 um tip deflection.
Methods that still raise on a default cantilever
These match MATLAB and are expected behaviour, not bugs. Each
computes a quantity that doesn't exist on the default configuration:
`tipDeflection` is a deliberate exception: it short-circuits to
`0.0` for `cantilever_type='none'` so the optimizer's
`TIP_DEFLECTION` metric is well-defined on the default.
Test plan
method either runs cleanly or raises with a helpful message that
matches MATLAB behaviour
method runs cleanly