Skip navigation
NASA Logo, National Aeronautics and Space Administration

Thoughts on Modeling with TDD

4 Posts tagged with the modeling tag
1

There are several ways of outlining the methodology used for scientific inquiry but it generally consists of the following basic steps:

 

1) Observe, collect data and ask critical questions

2) Formulate hypothetical explanations to answer tose critical questions

3) Make predictions from the hypotheses, i.e. develop a theory

4) Experiments (tests) of all of the above

 

In practice this method works as follows: given a theory of some phenomenon in the world we test it with experiments checking if the responses of the world correspond to the predictions of the current theory. Discrepancies give rise to an iterative cycle of theory refinement as new hypotheses are formulated and new experiments are performed.

 

With TDD [1], frequent micro-iterations are used to develop software:

 

1) Choose an area of the software design to work on.
2) A concrete example, as an executable test to drive the development, is designed, written and shown to fail
3) The code is extended to pass the test (while continuing to pass all other existing tests)

4) The code and tests are refactored to eliminate redundancy

 

Where I have made an effort to provide an analogy between the "four steps" of the scientific method and the "four steps" in TDD.

 

Note that tests in TDD take the role of experiments, while design takes the role of theory. Experimental reproducibility is managed through continued use of automated tests to ensure that the theory has not been broken. The program theory is driven through experimentation with tests. The theory is refined to fit the tests, with refactoring to ensure suitable generality.

 

It would seem that at first glance that the scientific method fits TDD rather well and, perhaps, there is value in using the scientific method as a methaphor to understand some aspects of TDD.

-----

 

References

 

[1] K. Beck. Test Driven Development: By Example. Addison Wesley, 2002.

[2] R. Mugridge, Test Driven Development and the Scientific Method, procs. XP2003, Genova, Italy, 2003

0

More on the decay model

Posted by Carlos Cruz Apr 17, 2010

I would like to elaborate on the question of what constitutes the simplest implementation, or as Tom puts it "how big of a step?".

 

Recall our test

! test condition that if k=0, yf=y0

   timeStep = 3600
   kappa = 0.0
   numSteps = 86400/timeStep
   allocate(y(numSteps))
   y0 = 1.0
   call Euler(y, y0, timeStep, numSteps)
   call assertEqual(y0, y(numSteps), tolerance=1e-8)

 

Let's start with the simplest implementation

   yf(numSteps) = y0

I would argue that this implementation  does not do very much (if anything at all).  This simply sets an output variable equals to an input variable. It does not test the algorithm we are trying to implement.

 

The other implementation

 

  yy0 = y0
   do i = 1,numSteps
      time = time + timeStep
      yf(i) = yy0*(1.0 - timeStep*kappa)
      yy0 = yf(i)
   end do

perhaps does too much. How can we be so confident that after numSteps our code will do what we intend it to do? Or that the timeStep will not violate the stability condition (this one is probably ok because we must choose timeStep < 1/kappa)?

 

Anyway, I would conclude that the implementation is as small as it can be and that it is the test that is not "properly" formulated. We should, at first, test what happens after one time step (and then two or three?). So, test and implement in small steps but let the smallness be determined by the task at hand.

0

Nearly all of the modeling of physical phenomena is based on conservation principles with the corresponding conservation equations represented by partial (or ordinary) differential equations. These equations are almost always impossible to solve analytically and thus one must immediately start choosing  numerical techniques appropriate for each problem.

 

In our first post we take a bird's eye view of modeling and choose the decay problem as a first example to demonstrate the use of TDD. In mathematical terms, this problem can be expressed as as an initial value problem:

 

dy(t)/dt = -ky, y(0)=y0    (1)

 

where y is concentration of a substance, t is time, and k is a positive constant parameter. Note that with k=0 there will be no change and y remains unchanged at its initial value. We also note that y will gradually decrease with time at a rate in proportion to concentration itself at any time instance.

 

So, how do we proceed in creating our first first numerical prediction model using TDD? What should be our first unit test? What should we test?

 

First we must decide what numerical method method to use. Let's start with the Euler method. In that case (1) can be discretized as follows:

 

y(n+1) = y(n)(1 + ht)     (2)

 

where h is the time step and n is the time index. We know from (1) that for k=0  y remains unchanged at its initial value so (2) must satisfy that condition. Let's create a module to contain our test:

 

Note: In this and all future posts we will be mainly making use of the fortran programming language and will be performing our unit tests under the pFUnit testing framework. For more information please see Modeling with TDD

 

module testIntegrators_mod
   use pFunit                ! unit testing framework
   use Integrators_mod       ! our code implememtation
   implicit none
   private
   public testEuler
   integer, parameter :: dp = selected_real_kind(14) ! 64 bit precision

contains

   subroutine testEuler
   implicit none
   ! y is the solution to the decay equation    
   real(kind=dp), allocatable, dimension(:) :: y
   real(kind=dp) :: y0       ! initial condition
   real(kind=dp) :: timeStep ! in seconds
   real(kind=dp) :: kappa    ! in seconds^-1
   integer :: numSteps
   integer :: i

! test condition that if k=0, yf=y0

   timeStep = 3600
   kappa = 0.0
   numSteps = 86400/timeStep
   allocate(y(numSteps))
   y0 = 1.0
   call Euler(y, y0, timeStep, numSteps)
   call assertEqual(y0, y(numSteps), tolerance=1e-8)
   deallocate(y)

   end subroutine testEuler

end module testIntegrators_mod

 

Note that our code implementation, Integrators_mod, does not yet exist and as such our code test module will not even compile! This is what TDD intends: let the unit testing drive the code development. However even at this point in our simple exercise several questions come to mind: how simple (or complex) should our first test be? what assumptions should be make as we write the test, say regarding  timeStep and numSteps? To what tolerance should be expect our result to be true? There is probably no clear-cut answer.

 

So now we must write the simplest unit of code that will make our test pass. Again, what constitues the simplest test? Here are two possibilities. First,

 

Module Integrators_mod
   implicit none
   private
   public Euler
   integer, parameter :: dp = selected_real_kind(14) ! 64 bit precision

contains

   subroutine Euler(yf, y0, timeStep, numSteps, kappa)
   real(kind=dp), intent(out) :: yf(:)
   real(kind=dp), intent(in)  :: timeStep
   real(kind=dp), intent(in)  :: y0
   real(kind=dp), intent(in)  :: kappa
   integer, intent(in)        :: numSteps

   integer :: i
   real :: time
   real :: yy0

   yy0 = y0
   do i = 1,numSteps
      time = time + timeStep
      yf(i) = yy0*(1.0 - timeStep*kappa)
      yy0 = yf(i)
   end do

   end subroutine Euler

end Module Integrators_mod

 

Second, we note that subroutine Euler could be as simple as

 

   subroutine Euler(yf, y0, timeStep, numSteps, kappa)
   real(kind=dp), intent(out) :: yf(:)
   real(kind=dp), intent(in)  :: timeStep
   real(kind=dp), intent(in)  :: y0
   real(kind=dp), intent(in)  :: kappa
   integer, intent(in)        :: numSteps
   yf(numSteps) = y0
   end subroutine Euler
 

 

and this will pass the test. What is the best approach? To be continued.

0

About this blog

Posted by Carlos Cruz Apr 15, 2010

Test-driven development (TDD) is a programming technique developed over the last decade that purports to generate "clean code that works". TDD turns around the basic approach of traditional software development, where requirements are turned into code (that is later tested and debugged), by emphasizing instead that the requirements be turned immediately into unit-tests that drive the actual code development! This revolutionary approach has proven to be quite successful in the software engineering community but its usefulness has not been tested (no pun intended) in the scientific modeling community. We create this blog to address this and other concerns.

 

Our approach will consist of developing examples of modeling with TDD starting with very simple models and working our way up to more complex (real life?) earth system models. As a result several blog entries will consist of code snippets whose objective will be to shed light into the TDD approach. We also hope that throughout this process we will generate a good dose of lively discussions that will allow us to gain insight on a subject matter, modeling with TDD, that is largely unexplored. Of course questions, comments and critiques are strongly encouraged.



USAGov logo NASA Logo - nasa.gov