Merge changes I930b316c,Iea4283e0,I103648f8 into release-4-6
authorSzilárd Páll <pszilard@kth.se>
Thu, 17 Jan 2013 22:18:55 +0000 (23:18 +0100)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Thu, 17 Jan 2013 22:18:55 +0000 (23:18 +0100)
* changes:
  BlueGene/Q installation notes
  fixed SD+BD integration slowing down with OpenMP threads
  Run regressiontests from build

CMakeLists.txt
admin/installguide/installguide.tex
src/mdlib/update.c
tests/CMakeLists.txt

index 0a39a467718a2abd4efab81605a40d873fc8bfcf..3a34b8b6a9cf463abe5cbb518a42e2b729db9ac3 100644 (file)
@@ -1136,8 +1136,17 @@ ADD_CUSTOM_TARGET(uninstall
 
 include(CTest)
 mark_as_advanced(BUILD_TESTING)
+#gmxtests target builds all binaries required for running gmxtest
+add_custom_target(gmxtests DEPENDS grompp mdrun pdb2gmx gmxcheck editconf)
 IF(BUILD_TESTING)
-       enable_testing()
-       add_subdirectory(tests)
+    enable_testing()
+    add_subdirectory(tests)
+    if(REGRESSIONTEST_PATH)
+        #check target builds all to run tests and the runs tests
+        add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure)
+        add_dependencies(check gmxtests)
+    else()
+        add_custom_target(check COMMAND ${CMAKE_COMMAND} -E echo "WARNING: No tests are run. Running the tests requires either of the cmake variables REGRESSIONTEST_PATH or REGRESSIONTEST_DOWNLOAD to be set.")
+    endif()
 ENDIF()
 
index 16d09c7e4acbee4b7e9a5c725c95043c3ea92c15..f5542465b1304705b561aa52385d38828e80b919 100644 (file)
@@ -531,12 +531,48 @@ Probably you need to build static libraries only? Volunteer needed.
 \subsubsection{BlueGene/P}
 
 Mark to write later. There is currently no native acceleration on this
-platform, but the default plain C kernels will work.
+platform, but the default plain C kernels will work. There will be
+accelerated Verlet kernels in 4.6 release.
 
 \subsubsection{BlueGene/Q}
 
-Mark to write later. There is currently no native acceleration on this
-platform, but the default plain C kernels will work.
+There is currently no native acceleration on this platform, but the
+default plain C kernels will work.
+
+Only static linking with XL compilers is supported by \gromacs{}. Dynamic
+linking would be supported by the architecture and \gromacs{}, but has no
+advantages other than disk space, and is generally discouraged on
+BlueGene for performance reasons.
+
+Computation on BlueGene floating-point units is always done in
+double-precision. However, single-precision builds of \gromacs{} are
+still normal and encouraged. The BlueGene hardware automatically
+converts values stored in single precision in memory to double
+precision in registers for computation, converts the results back to
+single precision correctly, and does so for no additional cost. As
+with other platforms, doing the whole computation in double precision
+normally shows no improvement in accuracy and costs twice as much time
+moving memory around.
+
+You need to arrange for FFTW to be installed correctly, following the
+above instructions.
+
+mpicc is used for compiling and linking. This can make it awkward to
+attempt to use IBM's optimized BLAS/LAPACK called ESSL. Since mdrun is
+the only part of \gromacs{} that should normally run on the compute
+nodes, and there is nearly no need for linear algebra support for
+mdrun, it is recommended to use the \gromacs{} built-in linear algebra
+routines.
+
+cmake .. -DCMAKE_TOOLCHAIN_FILE=BlueGeneQ-static-XL-C -DCMAKE_PREFIX_PATH=/your/fftw/installation/prefix
+make mdrun
+make install-mdrun
+
+It is possible to configure and make the remaining \gromacs{} tools
+with the compute node toolchain, but as none of those tools are
+\mpi{}-aware, this would not normally be useful. Instead, these should
+be planned to run on the login node, and a seperate \gromacs{}
+installation performed for that using the login node's toolchain.
 
 \section{Tested platforms}
 
index ff7d08fff0347493598910c65256115cae7d55f6..13e9ddb7e2bc2657a375ae86775bf48dcad37ba6 100644 (file)
@@ -70,6 +70,7 @@
 #include "orires.h"
 #include "gmx_wallcycle.h"
 #include "gmx_omp_nthreads.h"
+#include "gmx_omp.h"
 
 /*For debugging, start at v(-dt/2) for velolcity verlet -- uncomment next line */
 /*#define STARTFROMDT2*/
@@ -92,8 +93,12 @@ typedef struct {
 } gmx_sd_sigma_t;
 
 typedef struct {
-  /* The random state */
-  gmx_rng_t gaussrand;
+  /* The random state for ngaussrand threads.
+   * Normal thermostats need just 1 random number generator,
+   * but SD and BD with OpenMP parallelization need 1 for each thread.
+   */
+  int ngaussrand;
+  gmx_rng_t *gaussrand;
   /* BD stuff */
   real *bd_rf;
   /* SD stuff */
@@ -469,11 +474,42 @@ static void do_update_visc(int start,int nrend,double dt,
     }
 }
 
-static gmx_stochd_t *init_stochd(FILE *fplog,t_inputrec *ir)
+/* Allocates and initializes sd->gaussrand[i] for i=1, i<sd->ngaussrand,
+ * Using seeds generated from sd->gaussrand[0].
+ */
+static void init_multiple_gaussrand(gmx_stochd_t *sd)
+{
+    int          ngr,i;
+    unsigned int *seed;
+
+    ngr = sd->ngaussrand;
+    snew(seed,ngr);
+
+    for(i=1; i<ngr; i++)
+    {
+        seed[i] = gmx_rng_uniform_uint32(sd->gaussrand[0]);
+    }
+
+#pragma omp parallel num_threads(ngr)
+    {
+        int th;
+
+        th = gmx_omp_get_thread_num();
+        if (th > 0)
+        {
+            /* Initialize on each thread to have thread-local memory alloced */
+            sd->gaussrand[th] = gmx_rng_init(seed[th]);
+        }
+    }
+
+    sfree(seed);
+}
+
+static gmx_stochd_t *init_stochd(FILE *fplog,t_inputrec *ir,int nthreads)
 {
     gmx_stochd_t *sd;
     gmx_sd_const_t *sdc;
-    int  ngtc,n;
+    int  ngtc,n,th;
     real y;
 
     snew(sd,1);
@@ -481,7 +517,26 @@ static gmx_stochd_t *init_stochd(FILE *fplog,t_inputrec *ir)
     /* Initiate random number generator for langevin type dynamics,
      * for BD, SD or velocity rescaling temperature coupling.
      */
-    sd->gaussrand = gmx_rng_init(ir->ld_seed);
+    if (ir->eI == eiBD || EI_SD(ir->eI))
+    {
+        sd->ngaussrand = nthreads;
+    }
+    else
+    {
+        sd->ngaussrand = 1;
+    }
+    snew(sd->gaussrand,sd->ngaussrand);
+
+    /* Initialize the first random generator */
+    sd->gaussrand[0] = gmx_rng_init(ir->ld_seed);
+
+    if (sd->ngaussrand > 1)
+    {
+        /* Initialize the rest of the random number generators,
+         * using the first one to generate seeds.
+         */
+        init_multiple_gaussrand(sd);
+    }
 
     ngtc = ir->opts.ngtc;
 
@@ -563,12 +618,38 @@ static gmx_stochd_t *init_stochd(FILE *fplog,t_inputrec *ir)
 
 void get_stochd_state(gmx_update_t upd,t_state *state)
 {
-    gmx_rng_get_state(upd->sd->gaussrand,state->ld_rng,state->ld_rngi);
+    /* Note that we only get the state of the first random generator,
+     * even if there are multiple. This avoids repetition.
+     */
+    gmx_rng_get_state(upd->sd->gaussrand[0],state->ld_rng,state->ld_rngi);
 }
 
 void set_stochd_state(gmx_update_t upd,t_state *state)
 {
-    gmx_rng_set_state(upd->sd->gaussrand,state->ld_rng,state->ld_rngi[0]);
+    gmx_stochd_t *sd;
+    int i;
+
+    sd = upd->sd;
+
+    gmx_rng_set_state(sd->gaussrand[0],state->ld_rng,state->ld_rngi[0]);
+
+    if (sd->ngaussrand > 1)
+    {
+        /* We only end up here with SD or BD with OpenMP.
+         * Destroy and reinitialize the rest of the random number generators,
+         * using seeds generated from the first one.
+         * Although this doesn't recover the previous state,
+         * it at least avoids repetition, which is most important.
+         * Exaclty restoring states with all MPI+OpenMP setups is difficult
+         * and as the integrator is random to start with, doesn't gain us much.
+         */
+        for(i=1; i<sd->ngaussrand; i++)
+        {
+            gmx_rng_destroy(sd->gaussrand[i]);
+        }
+
+        init_multiple_gaussrand(sd);
+    }
 }
 
 gmx_update_t init_update(FILE *fplog,t_inputrec *ir)
@@ -579,7 +660,7 @@ gmx_update_t init_update(FILE *fplog,t_inputrec *ir)
 
     if (ir->eI == eiBD || EI_SD(ir->eI) || ir->etc == etcVRESCALE || ETC_ANDERSEN(ir->etc))
     {
-        upd->sd = init_stochd(fplog,ir);
+        upd->sd = init_stochd(fplog,ir,gmx_omp_nthreads_get(emntUpdate));
     }
 
     upd->xp = NULL;
@@ -592,7 +673,8 @@ gmx_update_t init_update(FILE *fplog,t_inputrec *ir)
 }
 
 static void do_update_sd1(gmx_stochd_t *sd,
-                          int start,int homenr,double dt,
+                          gmx_rng_t gaussrand,
+                          int start,int nrend,double dt,
                           rvec accel[],ivec nFreeze[],
                           real invmass[],unsigned short ptype[],
                           unsigned short cFREEZE[],unsigned short cACC[],
@@ -603,7 +685,6 @@ static void do_update_sd1(gmx_stochd_t *sd,
 {
   gmx_sd_const_t *sdc;
   gmx_sd_sigma_t *sig;
-  gmx_rng_t gaussrand;
   real   kT;
   int    gf=0,ga=0,gt=0;
   real   ism,sd_V;
@@ -611,12 +692,11 @@ static void do_update_sd1(gmx_stochd_t *sd,
 
   sdc = sd->sdc;
   sig = sd->sdsig;
-  if (homenr > sd->sd_V_nalloc)
+  if (nrend-start > sd->sd_V_nalloc)
   {
-      sd->sd_V_nalloc = over_alloc_dd(homenr);
+      sd->sd_V_nalloc = over_alloc_dd(nrend-start);
       srenew(sd->sd_V,sd->sd_V_nalloc);
   }
-  gaussrand = sd->gaussrand;
 
   for(n=0; n<ngtc; n++)
   {
@@ -625,7 +705,7 @@ static void do_update_sd1(gmx_stochd_t *sd,
       sig[n].V  = sqrt(kT*(1 - sdc[n].em*sdc[n].em));
   }
 
-  for(n=start; n<start+homenr; n++)
+  for(n=start; n<nrend; n++)
   {
       ism = sqrt(invmass[n]);
       if (cFREEZE)
@@ -662,8 +742,10 @@ static void do_update_sd1(gmx_stochd_t *sd,
   }
 }
 
-static void do_update_sd2(gmx_stochd_t *sd,gmx_bool bInitStep,
-                          int start,int homenr,
+static void do_update_sd2(gmx_stochd_t *sd,
+                          gmx_rng_t gaussrand,
+                          gmx_bool bInitStep,
+                          int start,int nrend,
                           rvec accel[],ivec nFreeze[],
                           real invmass[],unsigned short ptype[],
                           unsigned short cFREEZE[],unsigned short cACC[],
@@ -679,7 +761,6 @@ static void do_update_sd2(gmx_stochd_t *sd,gmx_bool bInitStep,
    * half of the update, needs to be remembered for the second half.
    */
   rvec *sd_V;
-  gmx_rng_t gaussrand;
   real   kT;
   int    gf=0,ga=0,gt=0;
   real   vn=0,Vmh,Xmh;
@@ -688,13 +769,12 @@ static void do_update_sd2(gmx_stochd_t *sd,gmx_bool bInitStep,
 
   sdc = sd->sdc;
   sig = sd->sdsig;
-  if (homenr > sd->sd_V_nalloc)
+  if (nrend-start > sd->sd_V_nalloc)
   {
-      sd->sd_V_nalloc = over_alloc_dd(homenr);
+      sd->sd_V_nalloc = over_alloc_dd(nrend-start);
       srenew(sd->sd_V,sd->sd_V_nalloc);
   }
   sd_V = sd->sd_V;
-  gaussrand = sd->gaussrand;
 
   if (bFirstHalf)
   {
@@ -709,7 +789,7 @@ static void do_update_sd2(gmx_stochd_t *sd,gmx_bool bInitStep,
       }
   }
 
-  for (n=start; n<start+homenr; n++)
+  for (n=start; n<nrend; n++)
   {
       ism = sqrt(invmass[n]);
       if (cFREEZE)
@@ -1299,7 +1379,7 @@ void update_tcouple(FILE         *fplog,
             break;
         case etcVRESCALE:
             vrescale_tcoupl(inputrec,ekind,dttc,
-                            state->therm_integral,upd->sd->gaussrand);
+                            state->therm_integral,upd->sd->gaussrand[0]);
             break;
         }
         /* rescale in place here */
@@ -1419,6 +1499,7 @@ void update_constraints(FILE         *fplog,
     int              start,homenr,nrend,i,n,m,g,d;
     tensor           vir_con;
     rvec             *vbuf,*xprime=NULL;
+    int              nth,th;
 
     if (constr) {bDoConstr=TRUE;}
     if (bFirstHalf && !EI_VV(inputrec->eI)) {bDoConstr=FALSE;}
@@ -1515,14 +1596,26 @@ void update_constraints(FILE         *fplog,
     {
         xprime = get_xprime(state,upd);
 
-        /* The second part of the SD integration */
-        do_update_sd2(upd->sd,FALSE,start,homenr,
-                      inputrec->opts.acc,inputrec->opts.nFreeze,
-                      md->invmass,md->ptype,
-                      md->cFREEZE,md->cACC,md->cTC,
-                      state->x,xprime,state->v,force,state->sd_X,
-                      inputrec->opts.ngtc,inputrec->opts.tau_t,
-                      inputrec->opts.ref_t,FALSE);
+        nth = gmx_omp_nthreads_get(emntUpdate);
+
+#pragma omp parallel for num_threads(nth) schedule(static)
+        for(th=0; th<nth; th++)
+        {
+            int start_th,end_th;
+
+            start_th = start + ((nrend-start)* th   )/nth;
+            end_th   = start + ((nrend-start)*(th+1))/nth;
+
+            /* The second part of the SD integration */
+            do_update_sd2(upd->sd,upd->sd->gaussrand[th],
+                          FALSE,start_th,end_th,
+                          inputrec->opts.acc,inputrec->opts.nFreeze,
+                          md->invmass,md->ptype,
+                          md->cFREEZE,md->cACC,md->cTC,
+                          state->x,xprime,state->v,force,state->sd_X,
+                          inputrec->opts.ngtc,inputrec->opts.tau_t,
+                          inputrec->opts.ref_t,FALSE);
+        }
         inc_nrnb(nrnb, eNR_UPDATE, homenr);
 
         if (bDoConstr)
@@ -1776,7 +1869,7 @@ void update_coords(FILE         *fplog,
         nth = gmx_omp_nthreads_get(emntUpdate);
     }
 
-# pragma omp parallel for num_threads(nth) schedule(static) private(alpha)
+#pragma omp parallel for num_threads(nth) schedule(static) private(alpha)
     for(th=0; th<nth; th++)
     {
         int start_th,end_th;
@@ -1810,7 +1903,8 @@ void update_coords(FILE         *fplog,
             }
             break;
         case (eiSD1):
-            do_update_sd1(upd->sd,start,homenr,dt,
+            do_update_sd1(upd->sd,upd->sd->gaussrand[th],
+                          start_th,end_th,dt,
                           inputrec->opts.acc,inputrec->opts.nFreeze,
                           md->invmass,md->ptype,
                           md->cFREEZE,md->cACC,md->cTC,
@@ -1821,7 +1915,8 @@ void update_coords(FILE         *fplog,
             /* The SD update is done in 2 parts, because an extra constraint step
              * is needed 
              */
-            do_update_sd2(upd->sd,bInitStep,start,homenr,
+            do_update_sd2(upd->sd,upd->sd->gaussrand[th],
+                          bInitStep,start_th,end_th,
                           inputrec->opts.acc,inputrec->opts.nFreeze,
                           md->invmass,md->ptype,
                           md->cFREEZE,md->cACC,md->cTC,
@@ -1830,13 +1925,13 @@ void update_coords(FILE         *fplog,
                           TRUE);
         break;
         case (eiBD):
-            do_update_bd(start,nrend,dt,
+            do_update_bd(start_th,end_th,dt,
                          inputrec->opts.nFreeze,md->invmass,md->ptype,
                          md->cFREEZE,md->cTC,
                          state->x,xprime,state->v,force,
                          inputrec->bd_fric,
                          inputrec->opts.ngtc,inputrec->opts.tau_t,inputrec->opts.ref_t,
-                         upd->sd->bd_rf,upd->sd->gaussrand);
+                         upd->sd->bd_rf,upd->sd->gaussrand[th]);
             break;
         case (eiVV):
         case (eiVVAK):
@@ -1942,7 +2037,7 @@ extern gmx_bool update_randomize_velocities(t_inputrec *ir, gmx_large_int_t step
             }
             upd->randatom_list_init = TRUE;
         }
-        andersen_tcoupl(ir,md,state,upd->sd->gaussrand,rate,
+        andersen_tcoupl(ir,md,state,upd->sd->gaussrand[0],rate,
                         (ir->etc==etcANDERSEN)?idef:NULL,
                         constr?get_nblocks(constr):0,
                         constr?get_sblock(constr):NULL,
index b7b44ddc7abb7ae3f30f4a85640f9ab15f5b6b78..edb5cf471b00f9473e36accca12c3b17aeac57cc 100644 (file)
 # To help us fund GROMACS development, we humbly ask that you cite
 # the research papers on the package. Check out http://www.gromacs.org.
 #
-enable_testing()
-add_test(TestExec_mdrun-h ../src/kernel/mdrun -h)
+set(REGRESSIONTEST_PATH "" CACHE PATH "Directory containing regressiontests")
+mark_as_advanced(REGRESSIONTEST_PATH)
+option(REGRESSIONTEST_DOWNLOAD
+    "Automatically download regressiontests. Tests can be run with ctest." no)
+if(REGRESSIONTEST_DOWNLOAD AND CMAKE_VERSION VERSION_LESS "2.8.2")
+    message(WARNING "REGRESSIONTEST_DOWNLOAD requires cmake >=2.8.2. Please update cmake or manually download the regressiontests.")
+    set(REGRESSIONTEST_DOWNLOAD FALSE CACHE BOOL 
+        "REGRESSIONTEST_DOWNLOAD not supported with cmake ${CMAKE_VERSION}" FORCE)
+endif()
+if(REGRESSIONTEST_DOWNLOAD)
+    if("${PROJECT_VERSION}" MATCHES "-dev")
+        set(REGRESSIONTEST_VERSION master)
+    else()
+        set(REGRESSIONTEST_VERSION ${PROJECT_VERSION})
+    endif()
+    set(REGRESSIONTEST_URL
+        http://gerrit.gromacs.org/download/regressiontests-${REGRESSIONTEST_VERSION}.tar.gz)
+    set(REGRESSIONTEST_FILE "${CMAKE_CURRENT_BINARY_DIR}/regressiontests.tgz")
+    message("Downloading: ${REGRESSIONTEST_URL}")
+    file(DOWNLOAD ${REGRESSIONTEST_URL} "${REGRESSIONTEST_FILE}" SHOW_PROGRESS STATUS status LOG log)
+    list(GET status 0 status_code)
+    list(GET status 1 status_string)
+    
+    if(NOT status_code EQUAL 0)
+        message(FATAL_ERROR "error: downloading '${REGRESSIONTEST_URL}' failed
+status_code: ${status_code}
+status_string: ${status_string}
+log: ${log}")
+    endif()
+
+    set(REGRESSIONTEST_PATH
+        "${CMAKE_CURRENT_BINARY_DIR}/regressiontests-${REGRESSIONTEST_VERSION}"
+        CACHE PATH "Path to auto-downloaded regressiontests" FORCE)
+    file(REMOVE_RECURSE "${REGRESSIONTEST_PATH}") #delete potential prior folder
+    execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${REGRESSIONTEST_FILE}"
+        WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+    if(NOT EXISTS ${REGRESSIONTEST_PATH}/gmxtest.pl)
+        message(FATAL_ERROR "Download incorrect. Doesn't contain required gmxtest.pl")
+    endif()
+    set(REGRESSIONTEST_DOWNLOAD OFF CACHE BOOL "Tests already downloaded. Set to yes to download again" FORCE)
+endif()
+
+if(REGRESSIONTEST_PATH AND (GMX_BLUEGENE OR CMAKE_CROSSCOMPILING OR CMAKE_CONFIGURATION_TYPES))
+    #Bluegene requires us to compile both front-end and back-end binaries (single build is insufficient)
+    message(WARNING 
+        "With cross-compiling or multi-configuration generators (e.g. Visual Studio), running regressiontests from build system is not supported. Please run gmxtest.pl directly.")
+    set(REGRESSIONTEST_PATH OFF CACHE BOOL 
+        "With cross-compiling or multi-configuration generators, running regressiontests from build system is not supported." FORCE)
+endif()
+
+if(REGRESSIONTEST_PATH)
+    if(NOT EXISTS ${REGRESSIONTEST_PATH}/gmxtest.pl)
+        message(FATAL_ERROR
+            "REGRESSIONTEST_PATH invalid. The path needs to contain gmxtest.pl.")
+    endif()
+    if(GMX_DOUBLE)
+        list(APPEND ARGS -double)
+    endif()
+    if(GMX_LIB_MPI AND NOT MPIEXEC) #autodetection failed or CC=mpicc was used
+        message(WARNING
+            "Please set MPIEXEC. Otherwise mpirun is assumed for runnings tests.")
+    endif()
+    if(GMX_LIB_MPI)
+        set(GMX_TEST_NUMBER_PROCS 8 CACHE STRING "Number of processors used for testing")
+        mark_as_advanced(GMX_TEST_NUMBER_PROCS)
+        list(APPEND ARGS -np ${GMX_TEST_NUMBER_PROCS})
+        if(MPIEXEC)
+            list(APPEND ARGS -mpirun ${MPIEXEC})
+        endif()
+        #We should use MPIEXEC_NUMPROC_FLAG but gmxtest.pl doesn't let us pass it
+    endif()
+    if(GMX_BINARY_SUFFIX)
+        list(APPEND ARGS -suffix ${GMX_BINARY_SUFFIX})
+    endif()
+    #crosscompile is only used to disable checking whether binaries work
+    #given that we know they are there and that mdrun might not be exectuable
+    #(e.g. Cray) we enable it.
+    list(APPEND ARGS -crosscompile)
+
+    set(REGRESSIONTEST_EXTRA_ARGS "" CACHE STRING 
+        "Extra arguments passed to gmxtest")
+    mark_as_advanced(REGRESSIONTEST_EXTRA_ARGS)
+    list(APPEND ARGS ${REGRESSIONTEST_EXTRA_ARGS})
+
+    list(APPEND ARGS -noverbose -nosuffix)
+
+    if(GMX_NATIVE_WINDOWS)
+        set(PATH_SEPARATOR "\\;")
+        #replacing \ with / shouldn't be neccessary. But otherwise "..\bin\;c:\.."
+        #gets turned into "...\bin\\c:\.." don't know why and don't have a better
+        #workaround. This workaround doesn't hurt.
+        string(REPLACE "\\" "/" PATH "$ENV{PATH}")
+        #protect ; (don't treat as list)
+        string(REPLACE ";" "\\;" PATH "${PATH}")
+    else()
+        set(PATH_SEPARATOR ":")
+        set(PATH "$ENV{PATH}")
+    endif()
+
+    foreach(FOLDER kernel tools gmxlib mdlib) #lib folders might be needed for
+        #e.g. DLLs. For GMX paths native ("\") is needed for GMXLIB detection
+        file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/src/${FOLDER}" DIR)
+        set(PATH "${DIR}${PATH_SEPARATOR}${PATH}")
+    endforeach()
+
+    find_program(PERL_EXECUTABLE NAMES "perl")
+    mark_as_advanced(PERL_EXECUTABLE)
+
+    if (NOT PERL_EXECUTABLE)
+        message(FATAL_ERROR "Perl not found. Install perl, set PERL_EXECUTABLE to the perl location, or unset REGRESSIONTEST_PATH to disable testing.")
+    endif()
+
+    #currently not testing tools because they don't contain any useful tests
+    foreach(subtest simple complex kernel freeenergy pdb2gmx)
+        add_test(NAME regressiontests/${subtest}
+            #windows requires the command to be perl and not the script
+            COMMAND perl "${REGRESSIONTEST_PATH}/gmxtest.pl" ${subtest} ${ARGS})
+        set_tests_properties(regressiontests/${subtest} PROPERTIES
+            ENVIRONMENT "PATH=${PATH}")
+    endforeach()
+endif()