The Problem
Standard C++ control flow (if/else, switch) doesn't work with symbolic types:
template <typename Scalar>
Scalar bad_example(const Scalar& x) {
if (x < 0) {
return -x;
}
return x;
}
The Solution: janus::where()
Use janus::where(condition, true_val, false_val) for symbolic-compatible branching:
template <typename Scalar>
Scalar good_example(const Scalar& x) {
}
auto where(const Cond &cond, const T1 &if_true, const T2 &if_false)
Select values based on condition (ternary operator) Returns: cond ? if_true : if_false Supports mixed...
Definition Logic.hpp:43
Pattern Guide
1. Simple If-Else
2. If-Else-If-Else (Using select - RECOMMENDED)
{Scalar(-1.0), Scalar(1.0)},
x);
Scalar(-1.0),
Scalar select(const std::vector< CondType > &conditions, const std::vector< Scalar > &values, const Scalar &default_value)
Multi-way conditional selection (cleaner alternative to nested where).
Definition Logic.hpp:547
3. Switch-Case Logic (select shines here!)
{Scalar(0.02), Scalar(0.025), Scalar(0.05)},
Scalar(0.03));
4. Complex Multi-Step Logic
When branches need multiple calculation steps, use helper functions:
template <typename Scalar>
Scalar turbulent_flow(const Scalar& re, const Scalar& v) {
auto correction = 1.0 + 0.144 *
janus::pow(v / 343.0, 2.0);
return cf * correction;
}
template <typename Scalar>
Scalar laminar_flow(const Scalar& re) {
}
template <typename Scalar>
Scalar skin_friction(const Scalar& re, const Scalar& v) {
turbulent_flow(re, v),
laminar_flow(re));
}
T sqrt(const T &x)
Computes the square root of a scalar.
Definition Arithmetic.hpp:46
T pow(const T &base, const T &exponent)
Computes the power function: base^exponent.
Definition Arithmetic.hpp:72
Important: Both branches are always evaluated in symbolic mode (that's how computational graphs work). The condition selects which result to use.
5. Multi-Variable Conditions
Scalar is_stalled = alpha_abs > alpha_stall;
Scalar low_reynolds = reynolds < 1e5;
correction_value,
nominal_value);
API Reference
janus::where(condition, true_val, false_val)
Basic ternary selection. Use for simple if-else.
janus::select(conditions, values, default_value)
Multi-way selection. Cleaner than nested where() for switch-like logic.
- conditions: Vector/list of conditions to check (in order)
- values: Vector/list of values to return (same size as conditions)
- default_value: Value if no condition matches
Returns the value corresponding to the first true condition, or default.
Key Takeaways
- Always use janus::where() for conditions involving template scalar types
- Nest where() calls for multi-way branching (if-else-if chains)
- Compute intermediate flags to make complex logic readable
- Works in both modes: numeric (evaluates immediately) and symbolic (builds graph)
- Enables autodiff: derivatives flow through all branches correctly
See Also