-
Notifications
You must be signed in to change notification settings - Fork 82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Event detection w/ ODEINT #203
Comments
The main problem here is that algorithms that use The idea is that the algorithm recorder ( // Custom kernel body will be recorded here:
std::ostringstream body;
vex::generator::set_recorder(body);
// Two sets of state variables to be able to track condition inside single kernel.
sym_state X_old = {
sym_value(sym_value::VectorParameter, sym_value::Const),
sym_value(sym_value::VectorParameter, sym_value::Const)
};
sym_state X_new = {
sym_value(sym_value::VectorParameter),
sym_value(sym_value::VectorParameter)
};
// Number of levels passed by each ball
vex::symbolic<int> sym_nlev(vex::symbolic<int>::VectorParameter);
// Next level for each of the balls:
vex::symbolic<double> sym_next_lev(vex::symbolic<double>::VectorParameter);
sym_value sym_gam(sym_value::VectorParameter, sym_value::Const);
// Non-adaptive stepper:
odeint::runge_kutta4<
sym_state, value_type, sym_state, value_type ,
odeint::range_algebra, odeint::default_operations
> sym_stepper;
sys_func sys(sym_gam);
// Start recording the kernel.
X_new[0] = X_old[0];
X_new[1] = X_old[1];
sym_stepper.do_step(std::ref(sys), X_new, 0, dt);
// Check if the next level has been passed, advance it in case it has:
vex::symbolic<bool> passed = (X_old[1] >= next_lev && next_lev <= X_new[1]);
// We can not use a conditional directly, but we can do this:
body << "if (" << passed /*this will dump the name of the generated variable*/ << ") {\n";
sym_nlev += 1;
sym_next_lev -= 10;
body << "}\n";
// Generate the kernel
auto kernel = vex::generator::build_kernel(ctx, "crossing", body.str(),
X_old[0], X_old[1], X_new[0], X_new[1], sym_gam,
sym_nlev, sym_next_lev
); I hope you will be able to work something out from this. |
That makes sense. Injecting code into the output stream is a pretty simple work around though.
Is there any reason why ...
vex::symbolic<float> sym_dt(vex::symbolic<float>::VectorParameter);
sym_stepper.do_step(std::ref(sys), X_new, 0, sym_dt);
// Check if the next level has been passed, advance it in case it has:
vex::symbolic<bool> passed = (X_old[0] >= next_lev && next_lev <= X_new[0]);
// We can not use a conditional directly, but we can do this:
if (pass) {
if(abs(X_new[0] - sym_next_lev) < 0.01) { // Within detection threshold
sym_nlev +=1;
sym_next_lev -=10;
dt = 0.1; // Reset time step to original step size;
} else {
X_new[0] = X_old[0];
X_new[1] = X_old[1]; //Reset state
dt /= 0.5; //Reduce step size
}
}
// Generate the kernel
auto kernel = vex::generator::build_kernel(ctx, "crossing", body.str(),
X_old[0], X_old[1], X_new[0], X_new[1], sym_gam,
sym_nlev, sym_next_lev,sym_dt
);
|
I don't see any right away, but with the logic you showed the update to |
Why is that? Wouldn't the updated dt persist between kernels calls? I would then do something like: vex::vector<value_type> dt(ctx, n);
vex::Reductor<float, vex::SUM> sum(ctx);
// Integration loop:
for(int ii = 0; ii < max_iterations; ++ii) {
kernel(..., dt);
if (sum(sym_nlvl) == num_lvls * n) // check if all num_lvls has been detected for all n systems
break;
}
I may have missed something, but I thought that is how the kernel would be used in your example. Were you thinking something else?
I guess in my example above, there really isn't a "load imbalance", but wasted computation. Example: I have n systems, but 1 of them descends much slower than the rest. Here, each system will continue to simulate past the last desired event until the last event for all the systems is detected. For my application, the time-scales between systems will not be drastically different. |
Ok, if you make dt a vector parameter, then sure.
I meant a loop inside the kernel, not the host-side loop that launches the kernel. |
Would you implement the loop in the kernel by manually updating the output stream, e.g. body << "for(int i = 0; i < max_iterations; ++i) { \n";
sym_stepper.do_step(std::ref(sys), X_new, 0, dt);
body <<"if( " << stopping_condition_here << ") {break;}";
... // make updates and adjust dt
body << "}\n"; Based on your experience do you think I would be best off trying the host- or device-side loop approach first? |
That was what I meant, yes.
I don't have any experience here, but I would say that (a) this needs some testing, and (b) host-side loop may be more safe, because long-running kernels may be a problem in some environments (e.g. windows watchdog process might kill any GPU kernel that runs for more than 5 seconds). |
I would like to use VexCL w/ ODEINT to do "event detection" or for finding "zero crossings", but I am struggling to determine the best way to do this.
I would like to use event detection for two different purposes: 1) detect a condition for stopping a simulation, e.g. drop a ball in the atmosphere and detect ground impact to stop simulation, 2) state the state of a system at specific state values, e.g. drop a ball in the atmosphere and record the state for every 10 m descended.
I started by trying to modify odeint-v2/examples/find_crossing.cpp using vex::symbolic (included below). I got the following compilation errors though:
The first error seems related to std::find_if. Are the 2nd two potentially related to headmyshoulder/odeint-v2#107?
After looking at the example some more, I think that this approach doesn't make much sense when using VexCL. One thought I had was to modify vexcl/examples/symbolic.cpp like this:
This seems like an ugly approach though. Do you have any suggestions or thoughts?
In the ideal world, I would like to do variable time step integration and be able to adjust the time steps in order to detect state events w/in a specified threshold.
The text was updated successfully, but these errors were encountered: