Implement short-circuit volume evaluation using infix notation
Based off some still unpublished but available research by @paulromano et al., there could be substantial savings in the way that we evaluate point-in-volume for initialization and surface crossing in ORANGE. (It's also used for distance-to-boundary for "complex" and "background" volumes since internal surface crossings have to be evaluated.)
I think we can break it down into the following steps:
- Develop an on-device
InfixEvaluatorthat applies a functionf(literal) -> bool(whereliteralusually means a surface index possibly with negation) and returns the evaulated result, using short circuit logic. (#1286) - Add a CSG "simplifier" that replaces negated
joins with the opposite kind ofjoinusing DeMorgan's laws. (#1289) - Add a utility to generate infix notation from the simplified tree (#1530)
- Add a utility to import from postfix notation (external JSON) to CSG tree for SCALE compatibility (@sethrj ) (#1582)
- Pass an infix logic representation rather than prefix through the OrangeInput into the params data (#1565)
After using the infix evalutor and comparing performance, then we can replace the "precalculate surface senses" operation with "on-the-fly surface senses". (#1539, #1569, #1576 )
Question for @paulromano : on-the-fly sense evaluation works for "point in volume" but might be difficult for "distance to boundary" in the case with internal surface crossings. To do that right now, we precalculate all the surfaces with the starting point, progressively move through nearest surfaces, flip the senses as we cross them, and evaluate "inside" at each one of those points. For the internal surface case, do you re-evaluate at every internal surface crossing? (I guess we could probably cache just a single surface state rather than all of them...?)
@esseivaju Regarding your questions today about infix:
- Let's get the building blocks in to the point where we can throw a switch to activate it.
- Add an enum field to the top-level ORANGE input for "infix"/"postfix"; if it's not the one that's used by the code, convert it when loading from disk in
OrangeParams::from_json. - We'll get a couple of @elliottbiondo's test problems (https://github.com/celeritas-project/docs/issues/12) in before doing performance comparisons
- Assuming infix is not slower (which would be a shock) we commit and close this out 😄
- Let's get the building blocks in to the point where we can throw a switch to activate it.
I think the question is how we'll want to do that The changes require updating the logic evaluator in SimpleUnitTracker, a runtime switch might require propagating templates many levels up.
@esseivaju Sorry I was very sloppy in how I phrased that. By "throw a switch" I meant e.g. "find-replace LogicEvaluator with InfixEvaluator" inside the runtime code. For point one, I didn't mean all the pieces in the simple unit tracker, I meant all the setup structure is well tested and working. I also didn't realize we nearly are at that point.
Before that point though:
- Define an
enum class LogicNotation { postfix, infix};, add an option to the InputBuilder to choose for the logic that's constructed, save that value in the top-level OrangeInput (which defaults to the legacypostfix). This allows us to add a few regression tests for theInputBuilderTestinsideorangeinp/UnitProtoand maybe ing4org/Converterto ensure they're constructing the correct infix trees and surfaces. We don't have to have those chosen at runtime. - Also add a
inline constexpr auto orange_tracking_logic = LogicNotation::postfixto OrangeData. In OrangeParams construction, assert that it's the same as the value that's in the input. This will catch if we try to run infix logic with our old postfix tracker. Add a static assertion inside SimpleUnitTracker next to where the logic evaluator is so we remember to keep it consistent. - Add the ability to take an existing ORANGE input and convert all the volumes between one type and the other. You've already got the infix/postfix converter in place I think. With this ability we can use the existing .org.json files and interface with SCALE.
- Finally after testing, apply your ~10-line diff to replace postfix with infix.
I think this should be a relatively minimal lift. If it's super simple, do the first two in one PR and the second two in another.