Task: Implement RHS.c for CactusTest/TestMoL thorn

thorn_name: CactusTest/TestMoL
src_filename: RHS.c
interface: implements: TestMoL
inherits: MethodOfLines # not really required but useful to make sure MoL is active

################################################################
### Aliased registration and type changing functions for the ###
### standard (i.e., real) GFs.                               ###
################################################################

CCTK_INT FUNCTION MoLRegisterEvolved(CCTK_INT IN EvolvedIndex, \
                                     CCTK_INT IN RHSIndex)
CCTK_INT FUNCTION MoLRegisterEvolvedSlow(CCTK_INT IN EvolvedIndex, \
                                         CCTK_INT IN RHSIndexSlow)

CCTK_INT FUNCTION MoLRegisterConstrained(CCTK_INT IN ConstrainedIndex)
CCTK_INT FUNCTION MoLRegisterSaveAndRestore(CCTK_INT IN SandRIndex)
CCTK_INT FUNCTION MoLRegisterEvolvedGroup(CCTK_INT IN EvolvedIndex, \
                                          CCTK_INT IN RHSIndex)
CCTK_INT FUNCTION MoLRegisterEvolvedGroupSlow(CCTK_INT IN EvolvedIndex, \
                                              CCTK_INT IN RHSIndexSlow)
CCTK_INT FUNCTION MoLRegisterConstrainedGroup(CCTK_INT IN ConstrainedIndex)
CCTK_INT FUNCTION MoLRegisterSaveAndRestoreGroup(CCTK_INT IN SandRIndex)
CCTK_INT FUNCTION MoLChangeToEvolved(CCTK_INT IN EvolvedIndex, \
                                     CCTK_INT IN RHSIndex)
CCTK_INT FUNCTION MoLChangeToEvolvedSlow(CCTK_INT IN EvolvedIndex, \
                                         CCTK_INT IN RHSIndexSlow)
CCTK_INT FUNCTION MoLChangeToConstrained(CCTK_INT IN ConstrainedIndex)
CCTK_INT FUNCTION MoLChangeToSaveAndRestore(CCTK_INT IN SandRIndex)
CCTK_INT FUNCTION MoLChangeToNone(CCTK_INT IN RemoveIndex)
CCTK_INT FUNCTION MoLQueryEvolvedRHS(CCTK_INT IN EvolvedIndex)
CCTK_INT FUNCTION MoLNumIntegratorSubsteps()

USES FUNCTION MoLRegisterEvolved
USES FUNCTION MoLRegisterEvolvedSlow
USES FUNCTION MoLRegisterConstrained
USES FUNCTION MoLRegisterSaveAndRestore
USES FUNCTION MoLRegisterEvolvedGroup
USES FUNCTION MoLRegisterEvolvedGroupSlow
USES FUNCTION MoLRegisterConstrainedGroup
USES FUNCTION MoLRegisterSaveAndRestoreGroup
USES FUNCTION MoLChangeToEvolved
USES FUNCTION MoLChangeToEvolvedSlow
USES FUNCTION MoLChangeToConstrained
USES FUNCTION MoLChangeToSaveAndRestore
USES FUNCTION MoLChangeToNone
USES FUNCTION MoLQueryEvolvedRHS
USES FUNCTION MoLNumIntegratorSubsteps

private:

CCTK_REAL evolved_gf TYPE = GF TIMELEVELS = 3 \
        "an evolved grid function"
CCTK_REAL rhs_gf TYPE = GF TIMELEVELS = 1 \
        "the rhs for the evolved grid function"
CCTK_REAL evolvedslow_gf TYPE = GF TIMELEVELS = 3 \
        "a slow evolved grid function"
CCTK_REAL rhsslow_gf TYPE = GF TIMELEVELS = 1 \
        "the rhs for the slow evolved grid function"
CCTK_REAL constrained_gf TYPE = GF TIMELEVELS = 3 \
        "a constrained grid function"
CCTK_REAL sandr_gf TYPE = GF TIMELEVELS = 3 \
        "a save-and-restore grid function"

CCTK_REAL diff_gf TYPE = GF TIMELEVELS = 1 \
        "difference to analytic solution in grid function"
CCTK_REAL analytic_gf TYPE = GF TIMELEVELS = 1 \
        "analytic solution in grid function"
CCTK_REAL diffslow_gf TYPE = GF TIMELEVELS = 1 \
        "difference to analytic solution in grid function"
CCTK_REAL analyticslow_gf TYPE = GF TIMELEVELS = 1 \
        "analytic solution in grid function"

CCTK_REAL evolved_ga TYPE = ARRAY DIM = 1 SIZE = 1 TIMELEVELS = 3 \
        DISTRIB=constant \
        "an evolved grid array"
CCTK_REAL rhs_ga TYPE = ARRAY DIM = 1 SIZE = 1 TIMELEVELS = 3 \
        DISTRIB=constant \
        "the rhs of the evolved grid array"
CCTK_REAL constrained_ga TYPE = ARRAY DIM = 1 SIZE = 1 TIMELEVELS = 3 \
        DISTRIB=constant \
        "a constrained grid array"
CCTK_REAL sandr_ga TYPE = ARRAY DIM = 1 SIZE = 1 TIMELEVELS = 3 \
        DISTRIB=constant \
        "a save-and-restore grid array"

CCTK_REAL diff_ga TYPE = ARRAY DIM = 1 SIZE = 1 TIMELEVELS = 3 \
        DISTRIB=constant \
        "difference to analytic solution in grid function"
CCTK_REAL analytic_ga TYPE = ARRAY DIM = 1 SIZE = 1 TIMELEVELS = 3 \
        DISTRIB=constant \
        "analytic solution in grid function"

schedule: STORAGE: evolved_gf[3] evolvedslow_gf[3] constrained_gf[3] sandr_gf[3]
STORAGE: rhs_gf[1] rhsslow_gf[1]
STORAGE: diff_gf[1] analytic_gf[1] diffslow_gf[1] analyticslow_gf[1]
STORAGE: evolved_ga[3] constrained_ga[3] sandr_ga[3]
STORAGE: rhs_ga[1]
STORAGE: diff_ga[1] analytic_ga[1]

schedule TestMoL_RegisterVars in MoL_Register
{
  LANG: C
  OPTIONS: META
} "Register evolved, rhs variables"

schedule TestMoL_InitVars AT INITIAL
{
  LANG: C
} "provide initial data for all variables"

schedule TestMoL_RHS_GF IN MoL_CalcRHS
{
  LANG: C
} "compute the RHS for the evolved grid function"

schedule TestMoL_RHSSlow_GF IN MoL_CalcRHS
{
  LANG: C
} "compute the RHS for the slow evolved grid function"

schedule TestMoL_RHS_GA IN MoL_CalcRHS
{
  LANG: C
} "compute the RHS for the evolved grid function"

schedule TestMoL_Compare_GF AT ANALYSIS
{
  LANG: C
} "Compare analytic and numerial result in grid function"

schedule TestMoL_Compare_GA AT ANALYSIS
{
  LANG: C
} "Compare analytic and numerial result in grid array"

param: private:

KEYWORD RHSexpression "expression to use for the right-hand-side of the ODE"
{
  "1" :: "unit rhs"
  "t" :: "linear in time rhs"
  "t**2" :: "quadratic in time rhs"
  "t**3" :: "cubic in time rhs"
  "t**4" :: "quartic in time rhs"
  "t**5" :: "quintic in time rhs"
  "t**6" :: "sixth order in time rhs"
  "t**7" :: "seventh order in time rhs"
  "t**8" :: "eight order in time rhs"
  "t**9" :: "ninth order in time rhs"
  "exp(t)" :: "exponential in time rhs"
} "1"

KEYWORD RHSSlowexpression "expression to use for the right-hand-side of the slow ODE"
{
  "1" :: "unit rhs"
  "t" :: "linear in time rhs"
  "t**2" :: "quadratic in time rhs"
  "t**3" :: "cubic in time rhs"
  "t**4" :: "quartic in time rhs"
  "t**5" :: "quintic in time rhs"
  "t**6" :: "sixth order in time rhs"
  "t**7" :: "seventh order in time rhs"
  "t**8" :: "eight order in time rhs"
  "t**9" :: "ninth order in time rhs"
  "exp(t)" :: "exponential in time rhs"
} "1"

# whenever we evolve a particular type we also register a SandR and constrained
# variable of the same type
BOOLEAN evolve_grid_function "register an evolved grid function with MoL"
{
} "yes"

BOOLEAN evolve_grid_array "register an evolved grid array with MoL"
{
} "yes"

configuration: # Configuration definitions for thorn TestMoL

context: Init.c:
```
#include "cctk.h"
#include "cctk_Parameters.h"
#include "cctk_Arguments.h"

#include <assert.h>

/*****************************************************************************
 ************** External routines ********************************************
 *****************************************************************************/
void TestMoL_InitVars(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_PARAMETERS;
  DECLARE_CCTK_ARGUMENTS;

  cGroupDynamicData groupdata;

  const int group = CCTK_GroupIndex(CCTK_THORNSTRING "::evolved_ga");
  const int ierr = CCTK_GroupDynamicData(cctkGH, group, &groupdata);
  if(ierr != 0)
  {
    CCTK_VError(__LINE__, __FILE__, CCTK_THORNSTRING,
                "Failed to obtain information about evolved_ga group: %d",
                ierr);
  }
  assert(groupdata.dim == 1);

  for(int i = 0 ; i < groupdata.lsh[0] ; i++)
  {
    evolved_ga[i] = -1.;
    sandr_ga[i] = 1.;
    sandr_ga_p[i] = 1.5;
    sandr_ga_p_p[i] = 1.75;
    constrained_ga[i] = 2.;
    constrained_ga_p[i] = 2.5;
    constrained_ga_p_p[i] = 2.75;
  }

#pragma omp parallel
  CCTK_LOOP3_ALL(TestMoL_InitVars, cctkGH, i,j,k)
  {
    const int idx = CCTK_GFINDEX3D(cctkGH, i,j,k);
    evolved_gf[idx] = -1.;
    evolvedslow_gf[idx] = -1.;
    sandr_gf[idx] = 1.;
    sandr_gf_p[idx] = 1.5;
    sandr_gf_p_p[idx] = 1.75;
    constrained_gf[idx] = 2.;
    constrained_gf_p[idx] = 2.5;
    constrained_gf_p_p[idx] = 2.75;
  } CCTK_ENDLOOP3_ALL(TestMoL_InitVars);
}

```

Register.c:
```
#include "cctk.h"
#include "cctk_Parameters.h"
#include "cctk_Arguments.h"

#include <assert.h>

/*****************************************************************************
 ************** External routines ********************************************
 *****************************************************************************/
void TestMoL_RegisterVars(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_PARAMETERS;

  if(evolve_grid_function)
  {
    const int evolvedslow_gf_group =
      CCTK_GroupIndex(CCTK_THORNSTRING "::evolvedslow_gf");
    const int rhsslow_gf_group =
      CCTK_GroupIndex(CCTK_THORNSTRING "::rhsslow_gf");
    const int evolved_gf_group =
      CCTK_GroupIndex(CCTK_THORNSTRING "::evolved_gf");
    const int rhs_gf_group = CCTK_GroupIndex(CCTK_THORNSTRING "::rhs_gf");
    const int constrained_gf_group =
      CCTK_GroupIndex(CCTK_THORNSTRING "::constrained_gf");
    const int sandr_gf_group = CCTK_GroupIndex(CCTK_THORNSTRING "::sandr_gf");

    const int ierr_evolved_gf_slow =
      MoLRegisterEvolvedGroupSlow(evolvedslow_gf_group, rhsslow_gf_group);
    const int ierr_evolved_gf =
      MoLRegisterEvolvedGroup(evolved_gf_group, rhs_gf_group);
    const int ierr_constrained_gf =
      MoLRegisterConstrainedGroup(constrained_gf_group);
    const int ierr_saveandrestore_gf =
      MoLRegisterSaveAndRestoreGroup(sandr_gf_group);

    assert(!ierr_evolved_gf_slow && !ierr_evolved_gf && !ierr_constrained_gf &&
           !ierr_saveandrestore_gf);
  }

  if(evolve_grid_array)
  {
    const int evolved_ga_group =
      CCTK_GroupIndex(CCTK_THORNSTRING "::evolved_ga");
    const int rhs_ga_group = CCTK_GroupIndex(CCTK_THORNSTRING "::rhs_ga");
    const int constrained_ga_group =
      CCTK_GroupIndex(CCTK_THORNSTRING "::constrained_ga");
    const int sandr_ga_group = CCTK_GroupIndex(CCTK_THORNSTRING "::sandr_ga");

    const int ierr_evolved_ga =
      MoLRegisterEvolvedGroup(evolved_ga_group, rhs_ga_group);
    const int ierr_constrained_ga =
      MoLRegisterConstrainedGroup(constrained_ga_group);
    const int ierr_saveandrestore_ga =
      MoLRegisterSaveAndRestoreGroup(sandr_ga_group);

    assert(!ierr_evolved_ga && !ierr_constrained_ga && !ierr_saveandrestore_ga);
  }

  // TODO: add code to test changing of variable types
  // TODO: add code to test upgrades from sandr -> constrained -> evolved
}

```

make.code.defn:
```
# Source files in this directory
SRCS = Register.c RHS.c Init.c

# Subdirectories containing source files
SUBDIRS = 

```


combined_doc_context: ## README (README):
```
Cactus Code Thorn TestMoL
Author(s)    : Roland Haas <rhaas@caltech.edu>
Maintainer(s): Roland Haas <rhaas@caltech.edu>
Licence      : LGPL
--------------------------------------------------------------------------

1. Purpose

Tests the ODE integration methods in MoL by providing a simple right-hand-side
and tests that exercise all integration methods.

```

## Documentation (.tex files):
Files: documentation.tex

```tex
=== documentation.tex ===
% *======================================================================*
%  Cactus Thorn template for ThornGuide documentation
%  Author: Ian Kelley
%  Date: Sun Jun 02, 2002
%  $Header$
%
%  Thorn documentation in the latex file doc/documentation.tex
%  will be included in ThornGuides built with the Cactus make system.
%  The scripts employed by the make system automatically include
%  pages about variables, parameters and scheduling parsed from the
%  relevant thorn CCL files.
%
%  This template contains guidelines which help to assure that your
%  documentation will be correctly added to ThornGuides. More
%  information is available in the Cactus UsersGuide.
%
%  Guidelines:
%   - Do not change anything before the line
%       % START CACTUS THORNGUIDE",
%     except for filling in the title, author, date, etc. fields.
%        - Each of these fields should only be on ONE line.
%        - Author names should be separated with a \\ or a comma.
%   - You can define your own macros, but they must appear after
%     the START CACTUS THORNGUIDE line, and must not redefine standard
%     latex commands.
%   - To avoid name clashes with other thorns, 'labels', 'citations',
%     'references', and 'image' names should conform to the following
%     convention:
%       ARRANGEMENT_THORN_LABEL
%     For example, an image wave.eps in the arrangement CactusWave and
%     thorn WaveToyC should be renamed to CactusWave_WaveToyC_wave.eps
%   - Graphics should only be included using the graphicx package.
%     More specifically, with the "\includegraphics" command.  Do
%     not specify any graphic file extensions in your .tex file. This
%     will allow us to create a PDF version of the ThornGuide
%     via pdflatex.
%   - References should be included with the latex "\bibitem" command.
%   - Use \begin{abstract}...\end{abstract} instead of \abstract{...}
%   - Do not use \appendix, instead include any appendices you need as
%     standard sections.
%   - For the benefit of our Perl scripts, and for future extensions,
%     please use simple latex.
%
% *======================================================================*
%
% Example of including a graphic image:
%    \begin{figure}[ht]
% 	\begin{center}
%    	   \includegraphics[width=6cm]{MyArrangement_MyThorn_MyFigure}
% 	\end{center}
% 	\caption{Illustration of this and that}
% 	\label{MyArrangement_MyThorn_MyLabel}
%    \end{figure}
%
% Example of using a label:
%   \label{MyArrangement_MyThorn_MyLabel}
%
% Example of a citation:
%    \cite{MyArrangement_MyThorn_Author99}
%
% Example of including a reference
%   \bibitem{MyArrangement_MyThorn_Author99}
%   {J. Author, {\em The Title of the Book, Journal, or periodical}, 1 (1999),
%   1--16. {\tt http://www.nowhere.com/}}
%
% *======================================================================*

% If you are using CVS use this line to give version information
% $Header$

\documentclass{article}

% Use the Cactus ThornGuide style file
% (Automatically used from Cactus distribution, if you have a
%  thorn without the Cactus Flesh download this from the Cactus
%  homepage at www.cactuscode.org)
\usepackage{../../../../doc/latex/cactus}

\begin{document}

% The author of the documentation
\author{Roland Haas \textless rhaas@caltech.edu\textgreater}

% The title of the document (not necessarily the name of the Thorn)
\title{TestMoL}

% the date your document was last changed, if your document is in CVS,
% please use:
%    \date{$ $Date: 2004-01-07 12:12:39 -0800 (Wed, 07 Jan 2004) $ $}
\date{February 12 2014}

\maketitle

% Do not delete next line
% START CACTUS THORNGUIDE

% Add all definitions used in this documentation here
%   \def\mydef etc

% Add an abstract for this thorn's documentation
\begin{abstract}
This thorn provides tests (partially correctness tests) for \texttt{MoL}.
It integrates a known function in time choosing for each ODE method a
polynomial in time that can be exactly integrated by the method.
\end{abstract}

% The following sections are suggestive only.
% Remove them or add your own.

\section{Introduction}
The method of lines thorn (\texttt{MoL}) provides time integration
facilities in Cactus. It basically is a wrapper for ODE integrators. As such
it can be tested by comparing its results against known analytic solutions.

\section{Physical System}
For grid functions we integrate
\begin{equation}
\dot y = -1 + t^n
\end{equation}
where $n$ is chosen such that a given ODE method can integrate the polynomial
exactly, eg. $n=3$ for the classical Runge-Kutta method. For multi-rate ODE
methods, the slow sector integrates
\begin{equation}
\dot y = -1 + \frac14 t^m
\end{equation}
where $m$ is usually smaller than $n$, eg. $2$ for the \texttt{RK4-RK2} scheme.
Finally grid arrays integrate
\begin{equation}
\dot y = -1 + \frac12 t^n
\end{equation}
where $n$ is the same as for (fast) grid functions.

\section{Numerical Implementation}
All numerical methods are provided by \texttt{MoL}. We construct the
numerical solution be evaluating the RHS on the full domain, no ghost or
boundary zones are required. We provide the numerical solution in
\texttt{evolved\_gf}, \texttt{evolvedslow\_gf}, \texttt{evolved\_ga} for the
grid function, slow sector grid function and grid array respectively. For each
type we provide the analytical solution in \texttt{analytic\_gf},
\texttt{analyticslow\_gf}, \texttt{analytic\_ga}. We provide the difference
between the two in \texttt{diff\_gf}, \texttt{diffslow\_gf},
\texttt{diff\_ga}.

Parameter files for the tests are generated using a perl script
\texttt{ODEs.pl} in the test directory. Its use is briefly explained in a
comment near the top of the file.

\section{Using This Thorn}
This thorn provides no functionality other than the tests.

\subsection{Obtaining This Thorn}
This thorn is part of the \texttt{CactusTest} arrangement and can be checked
out from the same source as Cactus.

\subsection{Interaction With Other Thorns}
This thorn requires \texttt{MoL}.

% Do not delete next line
% END CACTUS THORNGUIDE

\end{document}

```



Reference solution:
src_code: #include "cctk.h"
#include "cctk_Parameters.h"
#include "cctk_Arguments.h"

#include <assert.h>
#include <math.h>

/*****************************************************************************
 ************** Local types **************************************************
 *****************************************************************************/
enum RHStype {
  unity, linear, quadratic, cubic, quartic, quintic, sixthorder,
  seventhorder, eighthorder, ninthorder, exponential
};

/*****************************************************************************
 ************** Local data ***************************************************
 *****************************************************************************/
#define COEFF_SLOWGF 0.25
#define COEFF_GF 1.0
#define COEFF_GA 0.5

/*****************************************************************************
 ************** Local routine prototypes *************************************
 *****************************************************************************/
static enum RHStype get_rhs_type(const char *rhstype);
static CCTK_REAL eval_rhs(CCTK_REAL cctk_time,
                          enum RHStype rhs_type,
                          CCTK_REAL coeff);
static CCTK_REAL eval_solution(CCTK_REAL cctk_time,
                               enum RHStype rhs_type,
                               CCTK_REAL coeff);
static int get_mol_slowstep(const cGH *cctkGH);

/*****************************************************************************
 ************** External rtouines  *******************************************
 *****************************************************************************/
void TestMoL_RHS_GF(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_PARAMETERS;
  DECLARE_CCTK_ARGUMENTS;

  const enum RHStype rhs_type = get_rhs_type(RHSexpression);

#pragma omp parallel
  CCTK_LOOP3_ALL(TestMoL_RHS, cctkGH, i,j,k)
  {
    const int idx = CCTK_GFINDEX3D(cctkGH, i,j,k);
    rhs_gf[idx] = eval_rhs(cctk_time, rhs_type, COEFF_GF);
  } CCTK_ENDLOOP3_ALL(TestMoL_RHS);

  // TODO: add code to compute the constrained vars
  // TODO: add code that uses the sandr vars to check that MoL correctly saves
  // and restores them
}

void TestMoL_RHSSlow_GF(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_PARAMETERS;
  DECLARE_CCTK_ARGUMENTS;

  if(!get_mol_slowstep(cctkGH))
    return;

  const enum RHStype rhsslow_type = get_rhs_type(RHSSlowexpression);

#pragma omp parallel
  CCTK_LOOP3_ALL(TestMoL_RHSSlow, cctkGH, i,j,k)
  {
    const int idx = CCTK_GFINDEX3D(cctkGH, i,j,k);
    rhsslow_gf[idx] = eval_rhs(cctk_time, rhsslow_type, COEFF_SLOWGF);
  } CCTK_ENDLOOP3_ALL(TestMoL_RHSSlow);
}

void TestMoL_RHS_GA(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_PARAMETERS;
  DECLARE_CCTK_ARGUMENTS;

  cGroupDynamicData groupdata;

  const int group = CCTK_GroupIndex(CCTK_THORNSTRING "::evolved_ga");
  const int ierr = CCTK_GroupDynamicData(cctkGH, group, &groupdata);
  if(ierr != 0)
  {
    CCTK_VError(__LINE__, __FILE__, CCTK_THORNSTRING,
                "Failed to obtain information about evolved_ga group: %d",
                ierr);
  }
  assert(groupdata.dim == 1);

  const enum RHStype rhs_type = get_rhs_type(RHSexpression);
  const CCTK_REAL coeff = COEFF_GA;
  for(int i = 0 ; i < groupdata.lsh[0] ; i++)
    rhs_ga[i] = eval_rhs(cctk_time, rhs_type, coeff);

  // TODO: add code to compute the constrained vars
}

void TestMoL_Compare_GF(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_PARAMETERS;
  DECLARE_CCTK_ARGUMENTS;

  const enum RHStype rhs_type = get_rhs_type(RHSexpression);
  const enum RHStype rhsslow_type = get_rhs_type(RHSSlowexpression);

#pragma omp parallel
  CCTK_LOOP3_ALL(TestMoL_Compare, cctkGH, i,j,k)
  {
    const int idx = CCTK_GFINDEX3D(cctkGH, i,j,k);
    analytic_gf[idx] = eval_solution(cctk_time, rhs_type, COEFF_GF);
    analyticslow_gf[idx] = eval_solution(cctk_time, rhsslow_type, COEFF_SLOWGF);
    diff_gf[idx] = evolved_gf[idx] - analytic_gf[idx];
    diffslow_gf[idx] = evolvedslow_gf[idx] - analyticslow_gf[idx];
  } CCTK_ENDLOOP3_ALL(TestMoL_Compare);
}

void TestMoL_Compare_GA(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_PARAMETERS;
  DECLARE_CCTK_ARGUMENTS;

  cGroupDynamicData groupdata;

  const int group = CCTK_GroupIndex(CCTK_THORNSTRING "::evolved_ga");
  const int ierr = CCTK_GroupDynamicData(cctkGH, group, &groupdata);
  if(ierr != 0)
  {
    CCTK_VError(__LINE__, __FILE__, CCTK_THORNSTRING,
                "Failed to obtain information about evolved_ga group: %d",
                ierr);
  }
  assert(groupdata.dim == 1);

  const enum RHStype rhs_type = get_rhs_type(RHSexpression);
  const CCTK_REAL coeff = COEFF_GA;
  for(int i = 0 ; i < groupdata.lsh[0] ; i++)
  {
    analytic_ga[i] = eval_solution(cctk_time, rhs_type, coeff);
    diff_ga[i] = evolved_ga[i] - analytic_ga[i];
  }

  // TODO: add code to compute the constrained vars
}

/*****************************************************************************
 ************** Local Routines ***********************************************
 *****************************************************************************/
static enum RHStype get_rhs_type(const char *rhstype)
{
  enum RHStype retval;

  if(CCTK_EQUALS(rhstype, "1"))
    retval = unity;
  else if(CCTK_EQUALS(rhstype, "t"))
    retval = linear;
  else if(CCTK_EQUALS(rhstype, "t**2"))
    retval = quadratic;
  else if(CCTK_EQUALS(rhstype, "t**3"))
    retval = cubic;
  else if(CCTK_EQUALS(rhstype, "t**4"))
    retval = quartic;
  else if(CCTK_EQUALS(rhstype, "t**5"))
    retval = quintic;
  else if(CCTK_EQUALS(rhstype, "t**6"))
    retval = sixthorder;
  else if(CCTK_EQUALS(rhstype, "t**7"))
    retval = seventhorder;
  else if(CCTK_EQUALS(rhstype, "t**8"))
    retval = eighthorder;
  else if(CCTK_EQUALS(rhstype, "t**9"))
    retval = ninthorder;
  else if(CCTK_EQUALS(rhstype, "exp(t)"))
    retval = exponential;
  else
    CCTK_VError(__LINE__, __FILE__, CCTK_THORNSTRING,
                "Unknown rhs expression '%s'", rhstype);
  return retval;
}


static CCTK_REAL eval_solution(CCTK_REAL cctk_time,
                               enum RHStype rhs_type,
                               CCTK_REAL coeff)
{
  CCTK_REAL retval;

  switch(rhs_type)
  {
    case unity:
      retval = -1. + coeff*cctk_time;
      break;
    case linear:
      retval = -1. + coeff*1./2*pow(cctk_time, 2);
      break;
    case quadratic:
      retval = -1. + coeff*1./3*pow(cctk_time, 3);
      break;
    case cubic:
      retval = -1. + coeff*1./4*pow(cctk_time, 4);
      break;
    case quartic:
      retval = -1. + coeff*1./5*pow(cctk_time, 5);
      break;
    case quintic:
      retval = -1. + coeff*1./6*pow(cctk_time, 6);
      break;
    case sixthorder:
      retval = -1. + coeff*1./7*pow(cctk_time, 7);
      break;
    case seventhorder:
      retval = -1. + coeff*1./8*pow(cctk_time, 8);
      break;
    case eighthorder:
      retval = -1. + coeff*1./9*pow(cctk_time, 9);
      break;
    case ninthorder:
      retval = -1. + coeff*1./10*pow(cctk_time, 10);
      break;
    case exponential:
      retval = -1. + coeff*exp(cctk_time);
      break;
    default:
      CCTK_ERROR("Unexpected rhs type. This should not happen");
      break;
  }

  return retval;
}
static CCTK_REAL eval_rhs(CCTK_REAL cctk_time,
                          enum RHStype rhs_type,
                          CCTK_REAL coeff)
{
  CCTK_REAL retval;

  switch(rhs_type)
  {
    case unity:
      retval = coeff;
      break;
    case linear:
      retval = coeff*cctk_time;
      break;
    case quadratic:
      retval = coeff*pow(cctk_time, 2);
      break;
    case cubic:
      retval = coeff*pow(cctk_time, 3);
      break;
    case quartic:
      retval = coeff*pow(cctk_time, 4);
      break;
    case quintic:
      retval = coeff*pow(cctk_time, 5);
      break;
    case sixthorder:
      retval = coeff*pow(cctk_time, 6);
      break;
    case seventhorder:
      retval = coeff*pow(cctk_time, 7);
      break;
    case eighthorder:
      retval = coeff*pow(cctk_time, 8);
      break;
    case ninthorder:
      retval = coeff*pow(cctk_time, 9);
      break;
    case exponential:
      retval = coeff*exp(cctk_time);
      break;
    default:
      CCTK_ERROR("Unexpected rhs type. This should not happen");
      break;
  }

  return retval;
}

static int get_mol_slowstep(const cGH *cctkGH)
{
  CCTK_INT *slowstep;
  slowstep = CCTK_VarDataPtr(cctkGH, 0, "MoL::MoL_SlowStep");
  assert(slowstep);
  return *slowstep;
}