solving ODE on microcontroller - microcontroller

I would like to solve two ODE first order on microcontroller. It has to be evaluated every 100ms
x'=-k_{1}\cdot (x-x_{ref})\cdot e^{-b\cdot ((x-x_{obs})^{2}+(y-y_{obs})^{2})}
y'=-k_{1}\cdot (y-y_{ref})\cdot e^{-b\cdot ((x-x_{obs})^{2}+(y-y_{obs})^{2})}
Basically i thought of using euler integration (Runge-Kute I)
y(k+1)=y(k)+f(k,y(k))*dT
I expect error to be < 0.001. How do i determine how many iterations i should run until i hit that error rate ?

I guess that x and y, as well as x_{ref}, y_{ref}, x_{obs}, y_{obs} are time dependent. This limits the number of ODE solver you can use. So it can be only the Euler method and a Runge-Kutta method of 2 order (I forgot the name), which evaluate the r.h.s of you ODE only at the time points x(t), x(t+dT)´,x(t+2dT)`,...
You can use classical step size control with these two methods. That is you make one with step with the Euler method and one step with the RK-II method. The difference between these two steps is an indicator for the error and can be used for classical step size control. Have a look at the Numerical Recipes for more details.

Related

openMDAO: Does the use of ExecComp maximum() interfere with constraints not being affected by design variables?

When running the optimization driver on a large model I recieve:
DerivativesWarning:Constraints or objectives [('max_current.current_constraint.current_constraint', inds=[0]), ('max_current.continuous_current_constraint.continuous_current_constraint', inds=[0])] cannot be impacted by the design variables of the problem.
I read the answer to a similar question posed here.
The values do change as the design variables change, and the two constraints are satisfied during the course of optimization.
I had assumed this was due to those components' ExecComp using a maximum(), as this is the only place in my model I use a maximum function, however when setting up a simple problem with a maximum() function in a similar manner I do not receive an error.
My model uses explicit components that are looped, there are connections in the bottom left of the N2 diagram and NLBGS is converging the whole model. I currently am thinking it is due to the use of only explicit components and the NLBGS instead of implicit components.
Thank you for any insight you can give in resolving this warning.
Below is a simple script using maximum() that does not report errors. (I was so sure that was it) As I create a minimum working example that gives the error in a similar way to my larger model I will upload it.
import openmdao.api as om
prob=om.Problem()
prob.driver = om.ScipyOptimizeDriver()
prob.driver.options['optimizer'] = 'SLSQP'
prob.driver.options['tol'] = 1e-6
prob.driver.options['maxiter'] = 80
prob.driver.options['disp'] = True
indeps = prob.model.add_subsystem('indeps', om.IndepVarComp())
indeps.add_output('x', val=2.0, units=None)
prob.model.promotes('indeps', outputs=['*'])
prob.model.add_subsystem('y_func_1',
om.ExecComp('y_func_1 = x'),
promotes_inputs=['x'],
promotes_outputs=['y_func_1'])
prob.model.add_subsystem('y_func_2',
om.ExecComp('y_func_2 = x**2'),
promotes_inputs=['x'],
promotes_outputs=['y_func_2'])
prob.model.add_subsystem('y_max',
om.ExecComp('y_max = maximum( y_func_1 , y_func_2 )'),
promotes_inputs=['y_func_1',
'y_func_2'],
promotes_outputs=['y_max'])
prob.model.add_subsystem('y_check',
om.ExecComp('y_check = y_max - 1.1'),
promotes_inputs=['*'],
promotes_outputs=['*'])
prob.model.add_constraint('y_check', lower=0.0)
prob.model.add_design_var('x', lower=0.0, upper=2.0)
prob.model.add_objective('x')
prob.setup()
prob.run_driver()
print(prob.get_val('x'))
There is a problem with the maximum function in this context. Technically a maximum function is not differentiable; at least not when the index of which value is max is subject to change. If the maximum value is not subject to change, then it is differentiable... but you didn't need the max function anyway.
One correct, differentiable way to handle a max when doing gradient based things is to use a KS function. OpenMDAO provides the KSComp which implements it. There are other kinds of functions (like p-norm that you could use as well).
However, even though maximum is not technically differentiable ... you can sort-of/kind-of get away with it. At least, numpy (which ExecComp uses) lets you apply complex-step differentiation to the maximum function and it seems to give a non-zero derivative. So while its not technically correct, you can maybe get rid of it. At least, its not likely to be the core of your problem.
You mention using NLBGS, and that you have components which are looped. Your test case is purely feed forward though (here is the N2 from your test case).
. That is an important difference.
The problem here is with your derivatives, not with the maximum function. Since you have a nonlinear solver, you need to do something to get the derivatives right. In the example Sellar optimization, the model uses this line: prob.model.approx_totals(), which tells OpenMDAO to finite-difference across the whole model (including the nonlinear solver). This is simple and keeps the example compact. It also works regardless of whether your components define derivatives or not. It is however, slow and suffers from numerical difficulties. So use on "real" problems at your own risk.
If you don't include that (and your above example does not, so I assume your real problem does not either) then you're basically telling OpenMDAO that you want to use analytic derivatives (yay! they are so much more awesome). That means that you need to have a Linear solver to match your nonlinear one. For most problems that you start out with, you can simply put a DirectSolver right at the top of the model and it will all work out. For more advanced models, you need a more complex linear solver structure... but thats a different question.
Give this a try:
prob.model.linear_solver = om.DirectSolver()
That should give you non-zero total derivatives regardless of whether you have coupling (loops) or not.

Why there is a difference in ODE solution using event function in R?

I am solving a complicated system of nonlinear ODE using R as part of my current project. In a testing phase, I implemented solution using lsoda method by two ways (a) Using event function (b) without event function. Both solution produces slightly different results. I don't understand why it is happening or how to minimize error?
I tried Lotka–Volterra equations by these two methods and still wondering why there is a difference in solution (see attached plot). Although the difference is very small, I want to minimize error which might be big for my project. As you can see, event function is not changing dependent variables but still producing some errors.
rm(list=ls())
library(deSolve);
#############################################################
predpreyLV<-function(t,y,p){
N<-y[1]
P<-y[2]
with(as.list(p),{
dNdt<- r*N*(1-(N/1000))-a*P*N
dPdt<- -b*P+f*P*N
return(list(c(dNdt,dPdt)))
})
}
#############################################################
eventFun<-function(t,y,p){
return (y)
}
#############################################################
r<-0.5; a<-0.01; f<-0.01; b<-0.2;
# Simulation without using event function
p<-c(r=r,a=a, b=b, f=f)
y0<-c(N=25, P=5)
times<-seq(0,1000,0.1)
out1<-ode(y=y0,times,predpreyLV, p,method="lsoda", atol=1e-6, rtol=1e-6)
# Simulation using event function
p<-c(r=r,a=a, b=b, f=f)
y0<-c(N=25, P=5)
times<-seq(0,1000,0.1)
out2<-ode(y=y0,times,predpreyLV, p,method="lsoda",
events=list(func=eventFun, time=times), atol=1e-6, rtol=1e-6)
plot(out1[,1],abs(out1[,2]-out2[,2]), type="l", ylab="Difference in size")
You have to take into account that if not specifically indicated, all solvers have adaptive step size. This means that the solver uses internal steps that are usually not visible to the user, and uses specific polynomial interpolations between the internal nodes to produce the user-requested values.
However, as events can change the state, the solver can not proceed from the next internal node, it has to restart the solution process from the event time with the new state returned from the event function as initial state.
This has several effects. The last step before the event is cut short. The restart process likely uses smaller, non-optimal step sizes. The initialization of a multi-step method as used in lsoda uses a different method, probably also with non-typical step sizes. lsoda also adapts the order of the multi-step method. This also has to be determined empirically, advancing the solver with below-optimal steps sizes in the process.
This all, while not changing the accuracy of the solution much, still leads to a different integration path in terms of step sizes and methods/orders. If the system has stiff regions, as can be the case for polynomial non-linear right sides, these small changes can be magnified to the observed scale.
Still, relative to the maximum size 1000 of N, an error of 0.3 has a relative size of 3e-5, well within the expectation for the given tolerances.
To simulate the effect of different integration control processes I ran the given system (with python-scipy's solve_ivp with method "LSODA") for different tolerance inputs tol in 1e-6, 1e-8, 1e-10 using absolute and relative tolerance parameters atol=1e-2*tol, rtol=tol. These were then compared to a "gold" solution produced with tolerance tol=1e-14. This gives the overlayed solution paths
where there are no visible differences in the curves. For the components themselves and their error, divided by the tolerance value, I get the plots
Again there are no visible differences in the overlay of the three solutions in the first plot. The scaled error plot shows large oscillations corresponding to rapid changes in the solutions.

what if the FD steps varied w.r.t output/input

I am using the finite difference scheme to find gradients.
Lets say i have 2 outputs (y1,y2) and 1 input (x) in a single component. And in advance I know that the sensitivity of y1 with respect to x is not same as the sensitivity of y2 to x. And thus i could potentially have two different steps for those as in ;
self.declare_partials(of=y1, wrt=x, method='fd',step=0.01, form='central')
self.declare_partials(of=y2, wrt=x, method='fd',step=0.05, form='central')
There is nothing that stops me (algorithmically) but it is not clear what would openmdao gradient calculation exactly do in this case?
does it exchange information from the case where the steps are different by looking at the steps ratios or simply treating them independently and therefore doubling computational time ?
I just tested this, and it does the finite difference twice with the two different step sizes, and only saves the requested outputs for each step. I don't think we could do anything with the ratios as you suggested, as the reason for using different stepsizes to resolve individual outputs is because you don't trust the accuracy of the outputs at the smaller (or large) stepsize.
This is a fair question about the effect of the API. In typical FD applications you would get only 1 function call per design variable for forward and backward difference and 2 function calls for central difference.
However in this case, you have asked for two different step sizes for two different outputs, both with central difference. So here, you'll end up with 4 function calls to compute all the derivatives. dy1_dx will be computed using the step size of .01 and dy2_dx will be computed with a step size of .05.
There is no crosstalk between the two different FD calls, and you do end up with more function calls than you would have if you just specified a single step size via:
self.declare_partials(of='*', wrt=x, method='fd',step=0.05, form='central')
If the cost is something you can bear, and you get improved accuracy, then you could use this method to get different step sizes for different outputs.

Handling rounding errors of exponential function in convex optimization for scheduling web crawler

I am writing web crawler scheduler and have run into problems. First I will describe how I'm trying to find optimal schedule for when my crawler is visiting the page and then I will present my problem.
Scheduler definition
Scheduler is based on this paper "Optimal crawling strategies for web search engines" by J.Wolf. The paper proposes that update times of web pages follow exponential distribution with parameter λ. The problem is finding optimal number of times xi, the page i will be crawled in time interval [0,T]. The function proposed is:
Because this function is convex and its input arguments xi is discrete this kind of problem can be solved using algorithm suggested by Fredrerickson and Johnson in "The Complexity of Selection and Ranking in X + Y and Matrices with sorted columns", that has time complexity O(max{N, log(R/N)}). The optimization algorithm solves the problem by finding N-th element in [RxN] matrix where element at position (i, j) is equal to derivation of j function with input argument x = i, where derivation dj(xi) is equal to:
Because function fi is convex that means that function di has property that is monotonically increasing (matrix has sorted columns).
Problems
I run into problems when evaluating derivation, because of rounding errors d(x+1) - d(x), did not have guarantee to be greater or equal to 0, and I'm not sure that values that I got from optimizer are optional values. Rounding errors happen because value of x can be only positive integers in range of 0 to few billions, therefor exponent in function f is either big negative number or extremely small number (-5000).
Failed Attempts
The first thing I tried, I downloaded arbitrary precision library. This solved my problem but the overhead of library is to big.
The second thing I tried was I expanded d and got function like:
and then I tried to compare dj(xi) and dk(xw) by comparing their terms individually and than try to deduce is dj is bigger or smaller or greater than dk. If I could compare derivation I could solve my problem because optimization algorithm does not need concrete values, instead it only need relations between values. I couldn't find the solution because the term w.
I also tried looking at log(dj(xi)) because log preserves function monotony, but log also had rounding errors and I couldn't compare log(dj) and log(dk) without computing the final values.
If anybody has any other solution that could potentially work I would be most graceful.

Why does lsoda (in R) fail to complete running duration, with warning messages?

I am writing a numerical model in R, for an ecological system, and solving it using "lsoda" from package deSolve.
My model has 14 state variables.
I define the model, set it up fine, and give time duration according to this:
nyears<-60
ndays<-nyears*365+1
times<-seq(0,nyears*365,by=1)
Rates of change of state variables (e.g. the rate of change of variable "A1" is "dA1")are calculated according to existing values for state variables (at time=t) and a set of parameters.
Simplified example:
dA1<-Tf*A1*(ImaxA*p_sub)
Where Tf, ImaxA and p_sub are parameters, and A1 is my state variable at time=t.
When I solve the model, I use the lsoda solver like this:
out<-as.data.frame(lsoda(start,times,model,parms))
Sometimes (depending on my parameter combinations), the model run completes over the entire duration I have specified, however sometimes it stops short of the mark (still giving me output up until the solver "crashes"). When it "crashes", this message is displayed:
DLSODA- At current T (=R1), MXSTEP (=I1) steps
taken on this call before reaching TOUT
In above message, I1 = 5000
In above message, R1 = 11535.5
Warning messages:
1: In lsoda(start, times, model, parms) :
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxsteps
2: In lsoda(start, times, model, parms) :
Returning early. Results are accurate, as far as they go
It commonly appears when one of the state variables is getting exponentially bigger, or is tending very near to zero, however sometimes it crashes when seemingly not much change is happening. I may be wrong, but is it due to the rate of change of state-variables becoming too large? If so, why might it also "crash" when there is not a fast rate of change?
Is there a way that I can make the solver complete its task with the specified parameter values, maybe with a more relaxed tolerance for error?
Thank you all for your contributions. I looked at some of the rates, and at the point of crashing, the model was switching between two metabolic states - and the fast rate of this binary switch caused the solver to stop - rejecting the solution because the rate of change was too large. I have fixed my model by introducing a gradual switch between states (with a logistic curve) instead of this binary switch. I aknowledge that I didn;t give enough info in the original question, so thanks for the help you offered!

Resources