SeExpr

Using Variable Blocks

You can still define resolveVar() and resolveFunc() methods in your custom expression classes, but you now also have the option to use variable blocks instead. These are useful for re-using variables and managing thread evaluation. Within your custom expression class, you would skip the usual resolveVar() and resolveFunc() implementations:

    class MyExpr:public SeExpr2::Expression {

    // These are now optional:
    //   resolveVar()
    //   resolveFunc()
    };

Here's how you might write an example function to run an expression that uses variable blocks. This example assumes you have a particle data struct p, with numParticles, numAttributes. A variable block contains variable names and types, but doesn't care what the values are.

    void f(const std::string& s, MyParticleData* p, int outputDim=3){

        // set up the bindings for each of the particle attributes
        SeExpr2::VarBlockCreator blockCreator;
        std::vector variableHandles;
        std::vector attrs;
        for(int i=0; inumAttrs(); i++) {
            MyParticleAttr attr;
            p->getAttrInfo(i,attr);
            attrs.push_back(attr);
            int handle = blockCreator.registerVariable(attr.name,
                                                       TypeVec(attr.dim));
            variableHandles.push_back(handle);
        }

        // set up binding for the output
        int outputVariableHandle=blockCreator.registerVariable("__output",
                                                               TypeVec(outputDim));

        // make an expression with the appropriate bindings
        MyExpr e(s);
        e.setDesiredType(TypeVec(outputDim));
        e.setVarBlockCreator(&blockCreator);
        if(!e.isValid()) throw std::runtime_error(e.parseError()):

        // create the variable block, set up pointers to data
        SeExpr2::VarBlock block = blockCreator.create();
        for(int i=0; inumAttrs(); i++) {
            block.Pointer(variableHandles[i]) = p->data(attrs[i]);
        }
        std::vector outputData(p->numParticles()*outputDim);
        block.Pointer(outputVariableHandle)=outputData.data();

        // evaluate multiple expressions at once; inlines expressions
        e.evalMultiple(&block, outputVariableHandle, 0, p->numParticles());
    }

To parallelize evaluation per particle, a simple parallel_for can be used:

        // evaluate multiple expressions at once, inlines expressions
        tbb::parallel_for(blocked_range(0,p->numParticles()),[](blocked_range r)) {
            VarBlock myBlock=block.clone(); //should be cheap
            e.evalMultiple(&myblock, outputVariableHandle, r.start, r.end);
        }