Search Unity

Question Help me tune numerically function misbehavior

Discussion in 'Scripting' started by capocchione, Jul 14, 2020.

  1. capocchione

    capocchione

    Joined:
    Dec 9, 2019
    Posts:
    47
    I am experiencing some numerical misbehavior, probably because I've made some error writing the scripts (I am a newbie).

    I wrote a code that makes a prefab "growing" following some mathematical rules. These rules are Ordinary Differential Equations solved by Runge-Kutta numerical method (https://www.codeguru.com/csharp/.ne...rticle.php/c4597/Numerical-Computing-in-C.htm)

    The growth script has a variable calculated as ODE solution each timestep:
    Code (CSharp):
    1. public void UpdateGrowth()
    2.     {
    3.  
    4.         if (timeManager.IsDayElapsed && timeManager.Seconds <= setup.SimulationTime)
    5.         {
    6.             if (internodeID <= setup.StaticGeneticInfo.MaxInternodeNumber)
    7.                 setup.Manager.InternodesCurrentLenght[internodeID - 1] = transform.localScale.y;
    8.  
    9.             float nextValue = nextStepValue.NextStepValue;
    10.             float dayCount = timeManager.Days;
    11.             annualLengthGrowth = Runge.runge(0.0f, 1.0f, nextValue, .1f, new Runge.Function(Equation.equation));
    12.            
    13.             annualGrowth = new Vector3(annualWidthGrowth, annualLengthGrowth, annualWidthGrowth);
    14.  
    15.             nextStepValue.NextStepValue = annualLengthGrowth;
    16.  
    17.             currentLenght = (annualGrowth.y);
    The classRunge is the one for Runge-Kutta numerical method:


    Code (CSharp):
    1. public class Runge
    2. {
    3.     //declare a delegate that takes a float and returns
    4.     public delegate float Function(float dayCount, float annualGrowth);
    5.     public static float runge(float a, float b, float value, float step, Function f)
    6.     {
    7.         float dayCount,  w, k1, k2, k3, k4;
    8.         dayCount = a;
    9.         w = value;
    10.         for (int i = 0; i < (b - a) / step; i++)
    11.         {
    12.             k1 = step * f(dayCount, w);
    13.             k2 = step * f(dayCount + step / 2f, w + k1 / 2f);
    14.             k3 = step * f(dayCount + step / 2f, w + k2 / 2f);
    15.             k4 = step * f(dayCount + step, w + k3);
    16.             w = w + (k1 + 2f * k2 + 2f * k3 + k4) / 6f;
    17.             dayCount = a + i * step;
    18.         }
    19.         return w;
    20.     }
    21. }
    The equation to solve is in equation script:


    Code (CSharp):
    1.     public void Update()
    2.     {
    3.         dayCount = time.Days;
    4.     }
    5.  
    6.     public static float equation(float dayCount, float annualGrowth)
    7.     {
    8.         float y_prime;
    9.  
    10.         float maxLenght = (1 - (setup.Manager.TotalLenght() / maxTreeHeight));
    11.         temp = (float)-Math.Pow(((180 - dayCount) / 180), 2) * a + a;
    12.         alfaT = temp / a;
    13.         y_prime = alfaT * annualGrowth * (1 - (annualGrowth / maxLenght));
    14.  
    15.         return y_prime;      
    16.     }
    While the daycount is given by timemanager script:

    Code (CSharp):
    1. private void Start()
    2.     {
    3.         isdayElapsed = false;
    4.     }
    5.  
    6.     private void Update()
    7.     {
    8.         rate += Time.deltaTime;
    9.  
    10.         if (rate > 1)
    11.         {
    12.             rate = 0;
    13.             seconds++;
    14.             days++;
    15.             isdayElapsed = true;
    16.         }
    17.  
    18.         if (days > 365)
    19.         {
    20.             days = 0;
    21.         }
    22.     }
    The issue I am experiencing is that in the equation script, the dayCount variable is passed from 0 to 1, then from 1 is "resetting" calculating back from 0. This made annualLenghtGrowth in growth script miscalculated. Of course it should continue with 2, 3 ,4 and so on until 365.
    It looks like is resetting each update while the dayCount is working fine.
    I can't figure out where and what is the problem, please help me.
    Thank you
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    Code (CSharp):
    1. annualLengthGrowth = Runge.runge(0.0f, 1.0f, nextValue, .1f, new Runge.Function(Equation.equation));
    Looks to me like your code is just always passing 0 and 1 into the function. Maybe you meant to pass the day count?
     
  3. capocchione

    capocchione

    Joined:
    Dec 9, 2019
    Posts:
    47
    the dayCount is passed via the Runge.Function. 0 and 1 is the range of Runge-Kutta method calculation



    public static float runge(float a, float b, float value, float step, Function f)


    Fourth order Runge Kutte method for y'=f(t,y) solves first order ODE in the interval (a,b) with a given initial condition (value) and fixed step (step) as seen in the link I provided in my first post
     
  4. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    I don't understand your function/math at all. But what I do see is this. You are getting the day count from
    timeManager
    :
    Code (CSharp):
    1. float dayCount = timeManager.Days;
    2. annualLengthGrowth = Runge.runge(0.0f, 1.0f, nextValue, .1f, new Runge.Function(Equation.equation));
    But as far as I can tell, the day count value is completely ignored. You recalculate it inside Runge.runge from
    a 
    So what is the point of the dayCount variable or timeManager?

    Part of the confusion here is probably from obscure variable names. You have variables like
    a
    w
    b
    what are the meanings of these variables? Hard to tell.

    You should sprinkle your code with Debug.Log statements to make sure variables are what you expect at certain points. Or better yet, use Visual Studio's debugger and step through the execution to see what's going wrong.
     
  5. capocchione

    capocchione

    Joined:
    Dec 9, 2019
    Posts:
    47
    That's what I did and how I recognized the strange behavior.
    In equation script, even if
    time.Days
    returns the correct value,
    dayCount
    is wrong


    a
    ;
    w
    ;
    b
    variables are just used in Runge-Kutta numerical method to solve the ODE (equation).
    To explain better, take a look at these screenshot:



    a
    and
    b
    is the given range (first bullet point in the screen t = 0....10);
    w
    is yn+1
    nextValue
    is the first initial condition (y0 in the screen above)
    step
    is the given step value (dt = 0.1 in the screen above)
     
  6. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    That's the thing though. You have multiple variables in your program called
    dayCount


    in your screenshot of the debugger, there is a static variable called
    dayCount
    and there is a function parameter for the
    equation
    function called
    dayCount
    . These are two completely different variables that have no relation to one another. When you have two variables with the same name in a scope, the variable that was declared in the smaller scope wins out. This means when you access
    dayCount
    in the
    equation
    function you can see the function parameter
    dayCount
    , but not the static variable
    dayCount
    .
     
  7. capocchione

    capocchione

    Joined:
    Dec 9, 2019
    Posts:
    47
    Ok, so (sorry for the stupid question) how could I "trace back" to see where the dayCount I pass as parameter to the function is?
     
  8. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    Not sure what you mean by that, but the easiest way to resolve your confusion will be to simply rename the static variable dayCount so you don't have any name conflicts. Then go back through your code and make sure you're using the correct variables everywhere you see dayCount.
     
  9. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    In debugger go one frame up the call stack. But it's in your Runge.runge function when you call the f() function look at what you are passing in. From there you see it comes from the "a" variable.