# engineering pack 46 nodes, in `noodlelab[engineering]` and above. ## Engineering/Control Analysis (node-engineering.bode_plot)= ### Bode Plot `engineering.bode_plot` Magnitude (dB) and phase (degrees) against frequency (rad/s), with the gain and phase margins read as for an open loop (NaN where the curve does not cross 0 dB or −180°). Link the open loop L(s), the controller and plant in series, to see how far the closed loop is from instability. **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `min_frequency` | `Quantity[rad/s]` | 0: automatic Default `'0.0 rad / s'`. | | `max_frequency` | `Quantity[rad/s]` | 0: automatic Default `'0.0 rad / s'`. | | `points` | `int` | Default `500`. | | `input` | `str` | Its name; empty: the first Default `''`. | | `output` | `str` | Its name; empty: the first Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `frequency` | `NDArray[float64]` | | | `magnitude_db` | `NDArray[float64]` | | | `phase_deg` | `NDArray[float64]` | | | `plot` | `Figure` | | | `gain_margin_db` | `float` | | | `phase_margin_deg` | `float` | | | `summary` | `dict[str, float]` | | (node-engineering.controllability)= ### Controllability & Observability `engineering.controllability` Whether the inputs can steer every state (controllable) and the outputs reveal every state (observable): the ranks of the controllability matrix [B AB A²B …] and the observability matrix [C; CA; CA²; …] against the number of states. Pole placement and LQR need a controllable system. **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | **Outputs** | Name | Type | Description | |---|---|---| | `controllable` | `bool` | | | `observable` | `bool` | | | `controllable_rank` | `int` | | | `observable_rank` | `int` | | | `states` | `int` | | | `controllability_matrix` | `NDArray[float64]` | | | `observability_matrix` | `NDArray[float64]` | | | `summary` | `dict[str, float]` | | (node-engineering.dc_gain)= ### DC Gain `engineering.dc_gain` The steady-state gain: the output for a constant unit input, once everything has settled, G(0) (or G(1) when discrete). Infinite with an integrator. **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `input` | `str` | Its name; empty: the first Default `''`. | | `output` | `str` | Its name; empty: the first Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `float` | | (node-engineering.impulse_response)= ### Impulse Response `engineering.impulse_response` The response to a unit impulse (a hammer blow): the system's own motion, which rings at its natural frequencies. ``peak`` is the value largest in size, with its sign. **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `duration` | `Quantity[s]` | 0 s: automatic, from the slowest pole Default `'0.0 s'`. | | `points` | `int` | Default `1000`. | | `input` | `str` | Its name; empty: the first Default `''`. | | `output` | `str` | Its name; empty: the first Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `t` | `Quantity[s]` | | | `y` | `NDArray[float64]` | | | `plot` | `Figure` | | | `peak` | `float` | | | `final_value` | `float` | | | `summary` | `dict[str, float]` | | (node-engineering.initial_response)= ### Initial Response `engineering.initial_response` The free motion from an initial state with no input: a structure released from a deflected shape. The states are those of the state-space model (Mass-Spring-Damper: positions, then velocities, in SI units). **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `initial_state` | `str` | One value per state, comma separated: 0.01, 0, 0, 0 Default `'1, 0'`. | | `duration` | `Quantity[s]` | 0 s: automatic, from the slowest pole Default `'0.0 s'`. | | `points` | `int` | Default `1000`. | | `output` | `str` | Its name; empty: the first Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `t` | `Quantity[s]` | | | `y` | `NDArray[float64]` | | | `plot` | `Figure` | | | `peak` | `float` | | | `final_value` | `float` | | | `summary` | `dict[str, float]` | | (node-engineering.nyquist_plot)= ### Nyquist Plot `engineering.nyquist_plot` The open loop L(jω) drawn in the complex plane for all frequencies. The closed loop (unity negative feedback) is stable when the curve encircles −1 anticlockwise once for each unstable open-loop pole: Z = N + P unstable closed-loop poles, with N the clockwise encirclements. **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `input` | `str` | Its name; empty: the first Default `''`. | | `output` | `str` | Its name; empty: the first Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `plot` | `Figure` | | | `encirclements` | `int` | | | `open_loop_unstable_poles` | `int` | | | `closed_loop_stable` | `bool` | | | `summary` | `dict[str, float]` | | (node-engineering.pole_zero_map)= ### Pole-Zero Map `engineering.pole_zero_map` The poles (×) and zeros (○) in the complex plane, and a table with each pole's natural frequency ωn, damping ratio ζ and time constant τ. Poles in the right half plane (outside the unit circle, when discrete) make the system unstable; lightly damped ones make it ring. **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `input` | `str` | Its name; empty: the first Default `''`. | | `output` | `str` | Its name; empty: the first Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `poles` | `NDArray[complex128]` | | | `zeros` | `NDArray[complex128]` | | | `table` | `DataFrame` | | | `plot` | `Figure` | | | `stable` | `bool` | | | `min_damping` | `float` | | | `dc_gain` | `float` | | | `summary` | `dict[str, float]` | | (node-engineering.root_locus)= ### Root Locus `engineering.root_locus` Where the closed-loop poles go as the gain K of the loop K L(s) rises from 0: they start at the open-loop poles (×) and end at its zeros (○) or run off to infinity. ``critical_gain`` is the smallest gain at which the closed loop turns unstable (infinite if it never does, NaN if it is never stable). **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `gain` | `float` | Mark the poles at this gain K Default `1.0`. | | `max_gain` | `float` | 0: automatic Default `0.0`. | | `input` | `str` | Its name; empty: the first Default `''`. | | `output` | `str` | Its name; empty: the first Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `plot` | `Figure` | | | `poles_at_gain` | `NDArray[complex128]` | | | `stable_at_gain` | `bool` | | | `critical_gain` | `float` | | | `summary` | `dict[str, float]` | | (node-engineering.simulate)= ### Simulate `engineering.simulate` The response to any input signal u(t), from rest: a measured road profile, a set-point schedule, a sine sweep. ``t`` in seconds (a plain array counts as seconds), one value of u per time. **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `t` | `Quantity \| NDArray[floating]` | Times, evenly spaced | | `u` | `NDArray[floating]` | | | `input` | `str` | Its name; empty: the first Default `''`. | | `output` | `str` | Its name; empty: the first Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `t` | `Quantity[s]` | | | `y` | `NDArray[float64]` | | | `plot` | `Figure` | | | `peak` | `float` | | | `final_value` | `float` | | | `summary` | `dict[str, float]` | | (node-engineering.stability_margins)= ### Stability Margins `engineering.stability_margins` How far an open loop L(s) is from instability once the loop is closed with unity negative feedback. The gain margin (dB) is how much the gain can rise, at the phase crossover (phase −180°); the phase margin (°) how much phase lag can be added, at the gain crossover (|L| = 1). Infinite when the curve never crosses. ``stability_margin`` is the closest the Nyquist curve comes to −1 (1 is far, 0 is unstable). **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `input` | `str` | Its name; empty: the first Default `''`. | | `output` | `str` | Its name; empty: the first Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `gain_margin_db` | `float` | | | `phase_margin_deg` | `float` | | | `gain_crossover` | `Quantity[rad/s]` | | | `phase_crossover` | `Quantity[rad/s]` | | | `stability_margin` | `float` | | | `closed_loop_stable` | `bool` | | | `summary` | `dict[str, float]` | | (node-engineering.step_response)= ### Step Response `engineering.step_response` The response to a unit step: overshoot (%), 10–90 % rise time, settling time (within ``settling`` % of the final value for good), time of the peak and final value. An unstable system has no final value, overshoot or settling time (NaN). The system comes from Transfer Function, Feedback and the other builders (its coefficients used to be typed here). **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `duration` | `Quantity[s]` | 0 s: automatic, from the slowest pole Default `'0.0 s'`. | | `points` | `int` | Default `1000`. | | `input` | `str` | Its name; empty: the first Default `''`. | | `output` | `str` | Its name; empty: the first Default `''`. | | `settling` | `float` | The settling band, % of the final value Default `2.0`. | **Outputs** | Name | Type | Description | |---|---|---| | `t` | `Quantity[s]` | | | `y` | `NDArray[float64]` | | | `plot` | `Figure` | | | `final_value` | `float` | | | `overshoot` | `float` | | | `rise_time` | `Quantity[s]` | | | `settling_time` | `Quantity[s]` | | | `peak_time` | `Quantity[s]` | | | `stable` | `bool` | | | `summary` | `dict[str, float]` | | ## Engineering/Control Design (node-engineering.lqr)= ### LQR `engineering.lqr` The linear-quadratic regulator: the state feedback u = −K x that minimises ∫ (xᵀQx + uᵀRu) dt. Larger Q entries hold those states closer; larger R entries spend less input. ``closed_loop`` is the system with the feedback in place, driven by a reference r added to the input. **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `Q` | `Quantity \| NDArray[floating] \| None` | State weights; empty: the identity Optional. | | `R` | `Quantity \| NDArray[floating] \| None` | Input weights; empty: the identity Optional. | **Outputs** | Name | Type | Description | |---|---|---| | `gain` | `NDArray[float64]` | | | `closed_loop` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `poles` | `NDArray[complex128]` | | | `summary` | `dict[str, float]` | | (node-engineering.pole_placement)= ### Pole Placement `engineering.pole_placement` The state feedback u = −K x that puts the closed-loop poles where you say: further left is faster, and a pair a ± bj has damping ratio −a/√(a² + b²). The system must be controllable. **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `poles` | `str` | One per state, comma separated: -2, -3 ± 1j Default `'-2, -3'`. | **Outputs** | Name | Type | Description | |---|---|---| | `gain` | `NDArray[float64]` | | | `closed_loop` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `poles` | `NDArray[complex128]` | | | `summary` | `dict[str, float]` | | ## Engineering/Controls (node-engineering.convert_system)= ### Convert System `engineering.convert_system` The same system as a transfer function or in state space. Without the optional slycot package, only single-input, single-output systems convert to a transfer function. **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `to` | `Literal['transfer function', 'state space']` | Default `'state space'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.discretize)= ### Discretize `engineering.discretize` A continuous system as a discrete one sampled every ``sample_time``, for a digital controller: G(s) becomes G(z). **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `sample_time` | `Quantity[s]` | Default `'0.01 s'`. | | `method` | `Literal['zoh', 'foh', 'tustin', 'matched', 'euler', 'backward_diff']` | zoh: a sample held until the next; tustin: the bilinear transform Default `'zoh'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.feedback)= ### Feedback `engineering.feedback` Close the loop around ``system``: G / (1 + G H) with negative feedback, the usual kind, where the output is compared with the set point. With unity feedback (no H) and the open loop L = C G linked, this is the closed loop T = L / (1 + L). **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `feedback` | `Any` | H(s) in the feedback path; empty: 1 (unity) Optional. | | `sign` | `Literal['negative', 'positive']` | Default `'negative'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.first_order_system)= ### First-Order System `engineering.first_order_system` K / (τ s + 1): a lag such as a heater, a tank or an actuator, reaching 63 % of its final value after one time constant. A delay is approximated by a Padé filter of ``pade_order``. **Inputs** | Name | Type | Description | |---|---|---| | `gain` | `float` | Default `1.0`. | | `time_constant` | `Quantity[s]` | Default `'1.0 s'`. | | `delay` | `Quantity[s]` | A dead time, as a Padé approximation Default `'0.0 s'`. | | `pade_order` | `int` | Default `3`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.mass_spring_damper)= ### Mass-Spring-Damper `engineering.mass_spring_damper` The state-space model of masses, springs and dampers, M x'' + C x' + K x = F, for Step Response, Feedback and the rest. The states are the positions x1… and velocities v1…, the inputs the forces F1… at ``force_at``, and the outputs as ``measure`` says. M in kg, K in N/m and C in N·s/m (other units are converted; plain numbers are SI). **Inputs** | Name | Type | Description | |---|---|---| | `mass` | `Quantity \| NDArray[floating]` | A matrix: a quantity holding a 2-D array, or plain numbers in SI units | | `stiffness` | `Quantity \| NDArray[floating]` | A matrix: a quantity holding a 2-D array, or plain numbers in SI units | | `damping` | `Quantity \| NDArray[floating] \| None` | A matrix: a quantity holding a 2-D array, or plain numbers in SI units Optional. | | `force_at` | `str` | Where forces act, by degree of freedom: 1, or 1, 2 Default `'1'`. | | `measure` | `str` | The outputs: x1 (position), v1 (velocity), a1 (acceleration) Default `'x1'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.minimal_realisation)= ### Minimal Realisation `engineering.minimal_realisation` The system without its cancelling poles and zeros: (s + 1)/((s + 1)(s + 2)) is 1/(s + 2). Without the optional slycot package, a state-space system must have one input and one output. **Inputs** | Name | Type | Description | |---|---|---| | `system` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `tolerance` | `float` | How close a pole and zero cancel Default `1e-06`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.pid_controller)= ### PID Controller `engineering.pid_controller` A PID controller, C(s) = Kp + Ki/s + Kd s/(Tf s + 1). The derivative is filtered, as any real one is: without the filter it would be improper, amplifying noise without limit. Gains are in consistent SI units. **Inputs** | Name | Type | Description | |---|---|---| | `kp` | `float` | Proportional gain Default `1.0`. | | `ki` | `float` | Integral gain, per second Default `0.0`. | | `kd` | `float` | Derivative gain, in seconds Default `0.0`. | | `derivative_filter` | `Quantity[s]` | Tf: the derivative acts through 1/(Tf s + 1) Default `'0.01 s'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.parallel)= ### Parallel `engineering.parallel` Two systems side by side on the same input, their outputs added (or b subtracted from a). **Inputs** | Name | Type | Description | |---|---|---| | `a` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `b` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `sign` | `Literal['add', 'subtract']` | Default `'add'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.second_order_system)= ### Second-Order System `engineering.second_order_system` The standard second-order system, K ωn² / (s² + 2ζωn s + ωn²): a mass on a spring and damper, an RLC circuit. Below ζ = 1 it overshoots. **Inputs** | Name | Type | Description | |---|---|---| | `natural_frequency` | `Quantity[rad/s]` | Default `'1.0 rad / s'`. | | `damping_ratio` | `float` | ζ: 1 is critical Default `0.5`. | | `gain` | `float` | Default `1.0`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.series)= ### Series `engineering.series` Systems one after another: the signal goes through a, then b (then c). A controller, an actuator and a plant in series make the open loop L(s). **Inputs** | Name | Type | Description | |---|---|---| | `a` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `b` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | | `c` | `LTI \| None` | A linear system: from Transfer Function, State Space, PID Controller... Optional. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.state_space)= ### State Space `engineering.state_space` A system in state space: x' = A x + B u, y = C x + D u. A is n×n, B n×m, C p×n and D p×m (zero when not linked). A in 1/time is taken in 1/s; the others in consistent SI units. **Inputs** | Name | Type | Description | |---|---|---| | `A` | `Quantity \| NDArray[floating]` | A matrix: a quantity holding a 2-D array, or plain numbers in SI units | | `B` | `Quantity \| NDArray[floating]` | A matrix: a quantity holding a 2-D array, or plain numbers in SI units | | `C` | `Quantity \| NDArray[floating]` | A matrix: a quantity holding a 2-D array, or plain numbers in SI units | | `D` | `Quantity \| NDArray[floating] \| None` | A matrix: a quantity holding a 2-D array, or plain numbers in SI units Optional. | | `sample_time` | `Quantity[s]` | 0 s: continuous; else discrete Default `'0.0 s'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.transfer_function)= ### Transfer Function `engineering.transfer_function` A transfer function G(s) = num(s) / den(s), typed as polynomial coefficients in descending powers of s: ``1, 2, 1`` is s² + 2s + 1. With a sample time it is a discrete G(z) instead. **Inputs** | Name | Type | Description | |---|---|---| | `numerator` | `str` | Coefficients in descending powers of s, e.g. 1, 2, 1 Default `'1'`. | | `denominator` | `str` | Coefficients in descending powers of s, e.g. 1, 2, 1 Default `'1, 0.8, 1'`. | | `sample_time` | `Quantity[s]` | 0 s: continuous (s); else discrete (z) Default `'0.0 s'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.transfer_function_expression)= ### Transfer Function (Expression) `engineering.transfer_function_expression` A transfer function typed as an expression in s, with other symbols taken from ``values`` (as plain numbers in SI units), such as ``K/(tau*s + 1)`` with K and tau from a Values node. **Inputs** | Name | Type | Description | |---|---|---| | `text` | `str` | A function of s: 10/(s*(s + 2)), K/(tau*s + 1) Default `'10/(s*(s + 2))'`. | | `values` | `SymbolValues \| None` | Optional. | | `variable` | `str` | Default `'s'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | (node-engineering.zero_pole_gain)= ### Zero-Pole-Gain `engineering.zero_pole_gain` A transfer function from its zeros, poles and gain: G(s) = k (s − z₁)(s − z₂)… / ((s − p₁)(s − p₂)…). ``a ± bj`` is a complex pair. **Inputs** | Name | Type | Description | |---|---|---| | `zeros` | `str` | Comma separated; empty: none Default `''`. | | `poles` | `str` | Comma separated: -1, -2 ± 3j Default `'-1, -2'`. | | `gain` | `float` | Default `1.0`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `LTI` | A linear system: from Transfer Function, State Space, PID Controller... | ## Engineering/Decibels (node-engineering.from_db)= ### From dB `engineering.from_db` The ratio a value in decibels stands for. **Inputs** | Name | Type | Description | |---|---|---| | `db` | `float` | | | `kind` | `Literal['power', 'amplitude']` | power: 10 log10; amplitude (voltage, pressure): 20 log10 Default `'power'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `float` | | (node-engineering.to_db)= ### To dB `engineering.to_db` A ratio in decibels: 10 log10 of a power ratio, 20 log10 of an amplitude ratio. Use a ratio to 1 mW for dBm, to 1 W for dBW. **Inputs** | Name | Type | Description | |---|---|---| | `ratio` | `float` | | | `kind` | `Literal['power', 'amplitude']` | power: 10 log10; amplitude (voltage, pressure): 20 log10 Default `'power'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `float` | | (node-engineering.db_budget)= ### dB Budget `engineering.db_budget` Add up gains and losses in dB, one per line (``#`` starts a comment). The table has the running total after each item, for a report. **Inputs** | Name | Type | Description | |---|---|---| | `items` | `str` | One per line: name and value in dB (losses negative) Default `'Transmit power 30 dBm\nCable loss -2\nAntenna gain 12\nPath loss -120'`. | | `extra` | `float` | Added to the total, e.g. from another node Default `0.0`. | **Outputs** | Name | Type | Description | |---|---|---| | `total` | `float` | | | `table` | `DataFrame` | | ## Engineering/Fluids (node-engineering.pipe_pressure_drop)= ### Pipe Pressure Drop `engineering.pipe_pressure_drop` The friction pressure drop along a straight, full, circular pipe, Δp = f (L / D) ρ v² / 2. The Darcy friction factor f is 64 / Re in laminar flow and from the Haaland equation otherwise. Roughness: about 0.0015 mm drawn tubing and plastic, 0.045 mm commercial steel, 0.26 mm cast iron. **Inputs** | Name | Type | Description | |---|---|---| | `flow_rate` | `Quantity[L/s]` | Default `'2.0 l / s'`. | | `diameter` | `Quantity[mm]` | Default `'50.0 mm'`. | | `length` | `Quantity[m]` | Default `'100.0 m'`. | | `roughness` | `Quantity[mm]` | Default `'0.045 mm'`. | | `density` | `Quantity[kg/m^3]` | Default `'998.0 kg / m ** 3'`. | | `viscosity` | `Quantity[Pa*s]` | Default `'0.001 Pa * s'`. | **Outputs** | Name | Type | Description | |---|---|---| | `pressure_drop` | `Quantity[kPa]` | | | `velocity` | `Quantity[m/s]` | | | `reynolds` | `float` | | | `friction_factor` | `float` | | | `regime` | `str` | | (node-engineering.reynolds_number)= ### Reynolds Number `engineering.reynolds_number` Re = ρ v L / μ, with ``length`` the pipe diameter (or a body's characteristic length), and the pipe-flow regime: laminar below 2300, turbulent above 4000. The defaults are water at 20 °C. **Inputs** | Name | Type | Description | |---|---|---| | `velocity` | `Quantity[m/s]` | | | `length` | `Quantity[m]` | Default `'0.05 m'`. | | `density` | `Quantity[kg/m^3]` | Default `'998.0 kg / m ** 3'`. | | `viscosity` | `Quantity[Pa*s]` | Default `'0.001 Pa * s'`. | **Outputs** | Name | Type | Description | |---|---|---| | `reynolds` | `float` | | | `regime` | `str` | | ## Engineering/Materials (node-engineering.material)= ### Material `engineering.material` Typical properties of a common material: stiffness, density, strength, thermal expansion and conductivity. For preliminary design: check a datasheet before relying on the strengths. **Inputs** | Name | Type | Description | |---|---|---| | `name` | `str` | Default `'Aluminium 6061-T6'`. | **Outputs** | Name | Type | Description | |---|---|---| | `youngs_modulus` | `Quantity[GPa]` | | | `poisson_ratio` | `float` | | | `density` | `Quantity[kg/m^3]` | | | `yield_strength` | `Quantity[MPa]` | | | `ultimate_strength` | `Quantity[MPa]` | | | `thermal_expansion` | `Quantity[1/K]` | | | `thermal_conductivity` | `Quantity[W/(m*K)]` | | | `properties` | `dict[str, Any]` | | (node-engineering.material_table)= ### Material Table `engineering.material_table` Every built-in material as a table, with specific stiffness and strength (per unit density), for filtering, plotting or a Decision Matrix. **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | ## Engineering/Sections (node-engineering.section_properties)= ### Section Properties `engineering.section_properties` Area, second moments of area, elastic section modulus (about x) and least radius of gyration. **Inputs** | Name | Type | Description | |---|---|---| | `shape` | `Literal['rectangle', 'hollow rectangle', 'circle', 'tube', 'I-beam']` | Default `'rectangle'`. | | `width` | `Quantity[mm]` | Default `'50.0 mm'`. | | `height` | `Quantity[mm]` | Default `'100.0 mm'`. | | `wall` | `Quantity[mm]` | Default `'5.0 mm'`. | | `web` | `Quantity[mm]` | Default `'5.0 mm'`. | **Outputs** | Name | Type | Description | |---|---|---| | `area` | `Quantity[mm^2]` | | | `ixx` | `Quantity[mm^4]` | | | `iyy` | `Quantity[mm^4]` | | | `section_modulus` | `Quantity[mm^3]` | | | `radius_of_gyration` | `Quantity[mm]` | | | `summary` | `dict[str, float]` | | ## Engineering/Structures (node-engineering.axial_stress)= ### Axial Stress `engineering.axial_stress` Direct stress under an axial force, σ = F / A (tension positive). **Inputs** | Name | Type | Description | |---|---|---| | `force` | `Quantity[N]` | | | `area` | `Quantity[mm^2]` | | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Quantity[MPa]` | | (node-engineering.beam)= ### Beam `engineering.beam` Maximum deflection, bending moment and shear of a prismatic beam, and its deflected shape. ``load`` is the total load: a point load at midspan (at the free end of a cantilever), or spread uniformly over the span. Fixed-fixed moments are the largest, at the supports. **Inputs** | Name | Type | Description | |---|---|---| | `length` | `Quantity[m]` | Default `'2.0 m'`. | | `load` | `Quantity[kN]` | Default `'5.0 kN'`. | | `youngs_modulus` | `Quantity[GPa]` | Default `'200.0 GPa'`. | | `second_moment` | `Quantity[mm^4]` | Default `'4000000.0 mm ** 4'`. | | `support` | `Literal['simply supported', 'cantilever', 'fixed-fixed']` | Default `'simply supported'`. | | `load_type` | `Literal['point', 'uniform']` | uniform: the load spread over the span Default `'point'`. | | `points` | `int` | Default `101`. | **Outputs** | Name | Type | Description | |---|---|---| | `max_deflection` | `Quantity[mm]` | | | `max_moment` | `Quantity[N*m]` | | | `max_shear` | `Quantity[N]` | | | `x` | `NDArray[float64]` | | | `deflection` | `NDArray[float64]` | | | `plot` | `Figure` | | | `summary` | `dict[str, float]` | | (node-engineering.bending_stress)= ### Bending Stress `engineering.bending_stress` The largest bending stress in a section, σ = M / Z. **Inputs** | Name | Type | Description | |---|---|---| | `moment` | `Quantity[N*m]` | | | `section_modulus` | `Quantity[mm^3]` | | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Quantity[MPa]` | | (node-engineering.euler_buckling)= ### Euler Buckling `engineering.euler_buckling` The elastic buckling load of a slender column, P = π² E I / (K L)², with the effective length factor K for the end conditions. Give the ``area`` for the critical stress and slenderness ratio K L / r; stocky columns (slenderness below about 100 in steel) yield before they buckle. **Inputs** | Name | Type | Description | |---|---|---| | `youngs_modulus` | `Quantity[GPa]` | | | `second_moment` | `Quantity[mm^4]` | | | `length` | `Quantity[m]` | | | `area` | `Quantity[mm^2] \| None` | Optional. | | `ends` | `Literal['pinned-pinned', 'fixed-free', 'fixed-pinned', 'fixed-fixed']` | Default `'pinned-pinned'`. | **Outputs** | Name | Type | Description | |---|---|---| | `critical_load` | `Quantity[kN]` | | | `critical_stress` | `Quantity[MPa]` | | | `slenderness` | `float` | | (node-engineering.safety_factor)= ### Safety Factor `engineering.safety_factor` How far a design is from failing: the factor of safety capacity / demand (e.g. yield strength / stress), and the margin of safety capacity / (required × demand) − 1, which passes when it is ≥ 0. **Inputs** | Name | Type | Description | |---|---|---| | `capacity` | `Quantity` | | | `demand` | `Quantity` | | | `required` | `float` | The factor the design needs Default `1.5`. | **Outputs** | Name | Type | Description | |---|---|---| | `factor` | `float` | | | `margin` | `float` | | | `passes` | `bool` | | (node-engineering.von_mises)= ### Von Mises Stress `engineering.von_mises` The equivalent (von Mises) stress of a plane stress state, to compare with the yield strength: √(σx² − σx σy + σy² + 3 τxy²). **Inputs** | Name | Type | Description | |---|---|---| | `sigma_x` | `Quantity[MPa]` | | | `sigma_y` | `Quantity[MPa]` | Default `'0.0 MPa'`. | | `tau_xy` | `Quantity[MPa]` | Default `'0.0 MPa'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Quantity[MPa]` | | ## Engineering/Thermal (node-engineering.convection)= ### Convection `engineering.convection` Heat flow from a surface to a fluid, Q = h A ΔT (Newton's law of cooling). Typical h: 5–25 W/(m²·K) still air, 10–200 forced air, 500–10 000 forced water. **Inputs** | Name | Type | Description | |---|---|---| | `coefficient` | `Quantity[W/(m^2*K)]` | Default `'10.0 W / K / m ** 2'`. | | `area` | `Quantity[m^2]` | Default `'1.0 m ** 2'`. | | `temperature_difference` | `Quantity[delta_degC]` | Default `'20.0 Δ°C'`. | **Outputs** | Name | Type | Description | |---|---|---| | `heat_flow` | `Quantity[W]` | | | `resistance` | `Quantity[K/W]` | | (node-engineering.pipe_conduction)= ### Pipe Conduction `engineering.pipe_conduction` Radial heat flow through a pipe wall or insulation layer, Q = 2π k L ΔT / ln(r₂ / r₁). **Inputs** | Name | Type | Description | |---|---|---| | `conductivity` | `Quantity[W/(m*K)]` | | | `inner_radius` | `Quantity[mm]` | Default `'25.0 mm'`. | | `outer_radius` | `Quantity[mm]` | Default `'50.0 mm'`. | | `length` | `Quantity[m]` | Default `'1.0 m'`. | | `temperature_difference` | `Quantity[delta_degC]` | Default `'20.0 Δ°C'`. | **Outputs** | Name | Type | Description | |---|---|---| | `heat_flow` | `Quantity[W]` | | | `resistance` | `Quantity[K/W]` | | (node-engineering.thermal_expansion)= ### Thermal Expansion `engineering.thermal_expansion` The change in length of a free bar, ΔL = α L ΔT. **Inputs** | Name | Type | Description | |---|---|---| | `length` | `Quantity[m]` | | | `expansion` | `Quantity[1/K]` | Default `'1.2e-05 / K'`. | | `temperature_change` | `Quantity[delta_degC]` | Default `'50.0 Δ°C'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Quantity[mm]` | | (node-engineering.wall_conduction)= ### Wall Conduction `engineering.wall_conduction` Heat flow through a plane wall, Q = k A ΔT / t, and its thermal resistance t / (k A) (add resistances in series for layered walls). **Inputs** | Name | Type | Description | |---|---|---| | `conductivity` | `Quantity[W/(m*K)]` | | | `thickness` | `Quantity[mm]` | Default `'100.0 mm'`. | | `area` | `Quantity[m^2]` | Default `'1.0 m ** 2'`. | | `temperature_difference` | `Quantity[delta_degC]` | Default `'20.0 Δ°C'`. | **Outputs** | Name | Type | Description | |---|---|---| | `heat_flow` | `Quantity[W]` | | | `resistance` | `Quantity[K/W]` | | ## Engineering/Trade (node-engineering.decision_matrix)= ### Decision Matrix `engineering.decision_matrix` Rank options (one per row) by a weighted sum of criteria. Each criterion column is scaled from 0 (worst option) to 1 (best), so units do not matter, then weighted by the magnitude of its weight. The score is out of 100. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `option` | `str` | Default `''`. | | `criteria` | `str` | column:weight, comma separated; a negative weight: lower is better Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `ranking` | `DataFrame` | | | `best` | `str` | | | `plot` | `Figure` | |