예제 #1
0
std::wstring 
CellMLModelDefinition::getModelAsCCode(void* _model,void* _annotations)
{
  iface::cellml_api::Model* model = static_cast<iface::cellml_api::Model*>(_model);
  iface::cellml_services::AnnotationSet* as = static_cast<iface::cellml_services::AnnotationSet*>(_annotations);
  std::wstring code;
  RETURN_INTO_OBJREF(cgb,iface::cellml_services::CodeGeneratorBootstrap,
    CreateCodeGeneratorBootstrap());
  RETURN_INTO_OBJREF(cg,iface::cellml_services::CodeGenerator,
    cgb->createCodeGenerator());
#if defined (CUSTOM_CODE_GENERATION)
  RETURN_INTO_OBJREF(ccg, iface::cellml_services::CustomGenerator,
      cg->createCustomGenerator(model));
  RETURN_INTO_OBJREF(cti, iface::cellml_services::ComputationTargetIterator,
      ccg->iterateTargets());
  while (true)
  {
    RETURN_INTO_OBJREF(ct, iface::cellml_services::ComputationTarget,
        cti->nextComputationTarget());
    if (ct == NULL) break;
    RETURN_INTO_WSTRING(flag,as->getStringAnnotation(ct->variable(),L"flag"));
    if ((flag == L"WANTED") || (ct->degree() > 0))
    {
      ccg->requestComputation(ct);
    }
    else if (flag == L"KNOWN")
    {
      ccg->markAsKnown(ct);
    }
  }
#endif
  /* The trunk MaLaES has been updated since the 1.5 release, so define a
   * "custom" MaLaES here
   */
  RETURN_INTO_OBJREF(mbs,iface::cellml_services::MaLaESBootstrap,
    CreateMaLaESBootstrap());
  RETURN_INTO_OBJREF(mt,iface::cellml_services::MaLaESTransform,
    mbs->compileTransformer(
      L"opengroup: (\r\n"
      L"closegroup: )\r\n"
      L"abs: #prec[H]fabs(#expr1)\r\n"
      L"and: #prec[20]#exprs[&&]\r\n"
      L"arccos: #prec[H]acos(#expr1)\r\n"
      L"arccosh: #prec[H]acosh(#expr1)\r\n"
      L"arccot: #prec[1000(900)]atan(1.0/#expr1)\r\n"
      L"arccoth: #prec[1000(900)]atanh(1.0/#expr1)\r\n"
      L"arccsc: #prec[1000(900)]asin(1/#expr1)\r\n"
      L"arccsch: #prec[1000(900)]asinh(1/#expr1)\r\n"
      L"arcsec: #prec[1000(900)]acos(1/#expr1)\r\n"
      L"arcsech: #prec[1000(900)]acosh(1/#expr1)\r\n"
      L"arcsin: #prec[H]asin(#expr1)\r\n"
      L"arcsinh: #prec[H]asinh(#expr1)\r\n"
      L"arctan: #prec[H]atan(#expr1)\r\n"
      L"arctanh: #prec[H]atanh(#expr1)\r\n"
      L"ceiling: #prec[H]ceil(#expr1)\r\n"
      L"cos: #prec[H]cos(#expr1)\r\n"
      L"cosh: #prec[H]cosh(#expr1)\r\n"
      L"cot: #prec[900(0)]1.0/tan(#expr1)\r\n"
      L"coth: #prec[900(0)]1.0/tanh(#expr1)\r\n"
      L"csc: #prec[900(0)]1.0/sin(#expr1)\r\n"
      L"csch: #prec[900(0)]1.0/sinh(#expr1)\r\n"
      L"diff: #lookupDiffVariable\r\n"
      L"divide: #prec[900]#expr1/#expr2\r\n"
      L"eq: #prec[30]#exprs[==]\r\n"
      L"exp: #prec[H]exp(#expr1)\r\n"
      L"factorial: #prec[H]factorial(#expr1)\r\n"
      L"factorof: #prec[30(900)]#expr1 % #expr2 == 0\r\n"
      L"floor: #prec[H]floor(#expr1)\r\n"
      L"gcd: #prec[H]gcd_multi(#count, #exprs[, ])\r\n"
      L"geq: #prec[30]#exprs[>=]\r\n"
      L"gt: #prec[30]#exprs[>]\r\n"
      L"implies: #prec[10(950)] !#expr1 || #expr2\r\n"
      L"int: #prec[H]defint(func#unique1, BOUND, CONSTANTS, RATES, VARIABLES, "
      L"#bvarIndex, pret)#supplement double func#unique1(double* BOUND, "
      L"double* CONSTANTS, double* RATES, double* VARIABLES, int* pret) { return #expr1; }\r\n"
      L"lcm: #prec[H]lcm_multi(#count, #exprs[, ])\r\n"
      L"leq: #prec[30]#exprs[<=]\r\n"
      L"ln: #prec[H]log(#expr1)\r\n"
      L"log: #prec[H]arbitrary_log(#expr1, #logbase)\r\n"
      L"lt: #prec[30]#exprs[<]\r\n"
      L"max: #prec[H]multi_max(#count, #exprs[, ])\r\n"
      L"min: #prec[H]multi_min(#count, #exprs[, ])\r\n"
      L"minus: #prec[500]#expr1 - #expr2\r\n"
      L"neq: #prec[30]#expr1 != #expr2\r\n"
      L"not: #prec[950]!#expr1\r\n"
      L"or: #prec[10]#exprs[||]\r\n"
      L"plus: #prec[500]#exprs[+]\r\n"
      L"power: #prec[H]pow(#expr1, #expr2)\r\n"
      L"quotient: #prec[1000(0)] (double)(((int)#expr2) == 0 ? #expr1 / 0.0 : (int)(#expr1) / (int)(#expr2))\r\n"
      L"rem: #prec[1000(0)] (double)(((int)#expr2) == 0 ? (#expr1) / 0.0 : (int)(#expr1) % (int)(#expr2))\r\n"
      L"root: #prec[1000(900)] pow(#expr1, 1.0 / #degree)\r\n"
      L"sec: #prec[900(0)]1.0 / cos(#expr1)\r\n"
      L"sech: #prec[900(0)]1.0 / cosh(#expr1)\r\n"
      L"sin: #prec[H] sin(#expr1)\r\n"
      L"sinh: #prec[H] sinh(#expr1)\r\n"
      L"tan: #prec[H] tan(#expr1)\r\n"
      L"tanh: #prec[H] tanh(#expr1)\r\n"
      L"times: #prec[900] #exprs[*]\r\n"
      L"unary_minus: #prec[950]- #expr1\r\n"
      L"units_conversion: #prec[500(900)]#expr1*#expr2 + #expr3\r\n"
      L"units_conversion_factor: #prec[900]#expr1*#expr2\r\n"
      L"units_conversion_offset: #prec[500]#expr1+#expr2\r\n"
      L"xor: #prec[25(30)] (#expr1 != 0) ^ (#expr2 != 0)\r\n"
      L"piecewise_first_case: #prec[1000(5)](#expr1 ? #expr2 : \r\n"
      L"piecewise_extra_case: #prec[1000(5)]#expr1 ? #expr2 : \r\n"
      L"piecewise_otherwise: #prec[1000(5)]#expr1)\r\n"
      L"piecewise_no_otherwise: #prec[1000(5)]0.0/0.0)\r\n"
      L"eulergamma: #prec[999]0.577215664901533\r\n"
      L"exponentiale: #prec[999]2.71828182845905\r\n"
      L"false: #prec[999]0.0\r\n"
      L"infinity: #prec[900]1.0/0.0\r\n"
      L"notanumber: #prec[999]0.0/0.0\r\n"
      L"pi: #prec[999] 3.14159265358979\r\n"
      L"true: #prec[999]1.0\r\n"));
  /* now can use the standard transformation?
  cg->transform(mt);
  */
  try
  {
#if defined (CUSTOM_CODE_GENERATION)
    RETURN_INTO_OBJREF(cci,iface::cellml_services::CustomCodeInformation,ccg->generateCode());
    printf("Constraint level = ");
    switch (cci->constraintLevel())
    {
    case iface::cellml_services::UNDERCONSTRAINED:
      printf("UNDERCONSTRAINED\n");
      break;
    case iface::cellml_services::UNSUITABLY_CONSTRAINED:
      printf("UNSUITABLY_CONSTRAINED\n");
      break;
    case iface::cellml_services::OVERCONSTRAINED:
      printf("OVERCONSTRAINED\n");
      break;
    case iface::cellml_services::CORRECTLY_CONSTRAINED:
      printf("CORRECTLY_CONSTRAINED\n");
      break;
    default:
      printf("Unkown value\n");
    }
    printf("Index count: %u\n", cci->indexCount());
    cti = already_AddRefd<iface::cellml_services::ComputationTargetIterator>(cci->iterateTargets());
    while (true)
    {
      RETURN_INTO_OBJREF(ct, iface::cellml_services::ComputationTarget,
                         cti->nextComputationTarget());
      if (ct == NULL)
        break;

      RETURN_INTO_OBJREF(cv, iface::cellml_api::CellMLVariable, ct->variable());
      RETURN_INTO_WSTRING(compname, cv->componentName());
      RETURN_INTO_WSTRING(varname, cv->name());
      printf("* Computation target %S/%S:%u:\n", compname.c_str(), varname.c_str(),
             ct->degree());
      printf("  => Type = ");
      switch (ct->type())
      {
      case iface::cellml_services::VARIABLE_OF_INTEGRATION:
        printf("VARIABLE_OF_INTEGRATION - was marked as independent.\n");
        break;
      case iface::cellml_services::CONSTANT:
        printf("CONSTANT - this should not happen!\n");
        break;
      case iface::cellml_services::STATE_VARIABLE:
        printf("STATE_VARIABLE - was requested, and is available.\n");
        break;
      case iface::cellml_services::ALGEBRAIC:
        printf("ALGEBRAIC - is used as an intermediate.\n");
        break;
      case iface::cellml_services::FLOATING:
        printf("FLOATING - unused and not requested.\n");
        break;
      case iface::cellml_services::LOCALLY_BOUND:
        printf("LOCALLY_BOUND - locally bound in expressions only.\n");
        break;
      case iface::cellml_services::PSEUDOSTATE_VARIABLE:
        printf("PSEUDOSTATE_VARIABLE - target was requested, but could "
               "not be computed from the independent variables and model.\n");
        break;
      default:
        printf("Unknown type!\n");
      }
      RETURN_INTO_WSTRING(targname, ct->name());
      printf("  => Name = %S\n", targname.c_str());
      printf("  => Index = %u\n", ct->assignedIndex());
    }
    // To do: Print output from cci->iterateTargets();
    RETURN_INTO_WSTRING(functionsString, cci->functionsString());
    printf("Functions: %S\n", functionsString.c_str());
    RETURN_INTO_WSTRING(codeS, cci->generatedCode());
    printf("Code: %S\n", codeS.c_str());
#else // CUSTOM_CODE_GENERATION
    // annotate the source variables in the model with the code-names based on existing annotations
    RETURN_INTO_OBJREF(cvbs,iface::cellml_services::CeVASBootstrap,CreateCeVASBootstrap());
    RETURN_INTO_OBJREF(cevas,iface::cellml_services::CeVAS,cvbs->createCeVASForModel(model));
    for (unsigned int i=0;i<cevas->length();i++)
    {
      RETURN_INTO_OBJREF(cvs,iface::cellml_services::ConnectedVariableSet,cevas->getVariableSet(i));
      RETURN_INTO_OBJREF(sv,iface::cellml_api::CellMLVariable,cvs->sourceVariable());
      RETURN_INTO_WSTRING(array,as->getStringAnnotation(sv,L"array"));
      RETURN_INTO_WSTRING(index,as->getStringAnnotation(sv,L"array_index"));
      if (!array.empty() && !index.empty())
      {
        std::wstring ename = array;
        ename += L"[";
        ename += index;
        ename += L"]";
        as->setStringAnnotation(sv,L"expression",ename.c_str());
        if (array == L"OC_STATE")
        {
          ename = std::wstring(L"OC_RATE");
          ename += L"[";
          ename += index;
          ename += L"]";
          as->setStringAnnotation(sv,L"expression_d1",ename.c_str());
        }
      }
    }
    cg->useCeVAS(cevas);
    cg->useAnnoSet(as);
    RETURN_INTO_OBJREF(cci,iface::cellml_services::CodeInformation,cg->generateCode(model));
    wchar_t* m = cci->errorMessage();
    if (!wcscmp(m,L""))
    {
      std::cout << "whoo hoo!" << std::endl;
      iface::cellml_services::ModelConstraintLevel mcl = cci->constraintLevel();
      if (mcl == iface::cellml_services::UNDERCONSTRAINED)
      {
        std::cerr << "Model is underconstrained" << std::endl;
      }
      else if (mcl == iface::cellml_services::OVERCONSTRAINED)
      {
        std::cerr << "Model is overconstrained" << std::endl;
      }
      else if (mcl == iface::cellml_services::UNSUITABLY_CONSTRAINED)
      {
        std::cerr << "Model is unsuitably constrained" << std::endl;
      }
      else
      {
        std::cout << "Model is correctly constrained" << std::endl;
        // create the code in the format we know how to handle
        code += L"#include <math.h>\n";
        code += L"#include <stdio.h>\n";
        /* required functions */
        code += L"extern double fabs(double x);\n";
        code += L"extern double acos(double x);\n";
        code += L"extern double acosh(double x);\n";
        code += L"extern double atan(double x);\n";
        code += L"extern double atanh(double x);\n";
        code += L"extern double asin(double x);\n";
        code += L"extern double asinh(double x);\n";
        code += L"extern double acos(double x);\n";
        code += L"extern double acosh(double x);\n";
        code += L"extern double asin(double x);\n";
        code += L"extern double asinh(double x);\n";
        code += L"extern double atan(double x);\n";
        code += L"extern double atanh(double x);\n";
        code += L"extern double ceil(double x);\n";
        code += L"extern double cos(double x);\n";
        code += L"extern double cosh(double x);\n";
        code += L"extern double tan(double x);\n";
        code += L"extern double tanh(double x);\n";
        code += L"extern double sin(double x);\n";
        code += L"extern double sinh(double x);\n";
        code += L"extern double exp(double x);\n";
        code += L"extern double floor(double x);\n";
        code += L"extern double pow(double x, double y);\n";
        code += L"extern double factorial(double x);\n";
        code += L"extern double log(double x);\n";
        code += L"extern double arbitrary_log(double x, double base);\n";
        code += L"extern double gcd_pair(double a, double b);\n";
        code += L"extern double lcm_pair(double a, double b);\n";
        code += L"extern double gcd_multi(unsigned int size, ...);\n";
        code += L"extern double lcm_multi(unsigned int size, ...);\n";
        code += L"extern double multi_min(unsigned int size, ...);\n";
        code += L"extern double multi_max(unsigned int size, ...);\n";
        code += L"extern void NR_MINIMISE(double(*func)"
          L"(double VOI, double *C, double *R, double *S, double *A),"
          L"double VOI, double *C, double *R, double *S, double *A, "
          L"double *V);\n";
        wchar_t* frag = cci->functionsString();
        code += frag;
        free(frag);

        nBound = 1;
        nRates = cci->rateIndexCount();
        nAlgebraic = cci->algebraicIndexCount();
        nConstants = cci->constantIndexCount();

        code += L"\n\nvoid OC_CellML_RHS_routine(double VOI, double* OC_STATE, double* OC_RATE, double* OC_WANTED, double* OC_KNOWN)\n{\n\n";
        code += L"double DUMMY_ASSIGNMENT;\n";
        code += L"double CONSTANTS[";
        code += formatNumber(nConstants);
        code += L"], ALGEBRAIC[";
        code += formatNumber(nAlgebraic);
        code += L"];\n\n";

        // start the model code...
        /* https://svn.physiomeproject.org/svn/physiome/CellML_DOM_API/trunk/interfaces/CCGS.idl for full description */

        /* initConsts - all variables which aren't state variables but have
         *              an initial_value attribute, and any variables & rates
         *              which follow.
         */
        frag = cci->initConstsString();
        //code += L"void SetupFixedConstants(double* CONSTANTS,double* RATES,"
        //  L"double* STATES)\n{\n";
        code += frag;
        //code += L"}\n";
        free(frag);

        /* rates      - All rates which are not static.
         */
        frag = cci->ratesString();
        //code += L"void ComputeRates(double VOI,double* STATES,double* RATES,"
        //  L"double* CONSTANTS,double* ALGEBRAIC)\n{\n";
        code += frag;
        //code += L"}\n";
        free(frag);

        /* variables  - All variables not computed by initConsts or rates
         *  (i.e., these are not required for the integration of the model and
         *   thus only need to be called for output or presentation or similar
         *   purposes)
         */
        frag = cci->variablesString();
        //code += L"void EvaluateVariables(double VOI,double* CONSTANTS,"
        //  L"double* RATES, double* STATES, double* ALGEBRAIC)\n{\n";
        code += frag;
        //code += L"}\n";
        free(frag);

        // and now clear out initialisation of state variables and known variables.
        clearCodeAssignments(code,L"OC_STATE",mStateCounter);
        clearCodeAssignments(code,L"OC_KNOWN",mParameterCounter);

        // close the subroutine
        code += L"\n\n}//OC_CellML_RHS_routine()\n\n;";
      }
    }
    else
    {
      std::wcerr << "Error generating code: " << m << std::endl;
    }
    free(m);
#endif // CUSTOM_CODE_GENERATION
  }
  catch (...)
  {
    std::wcerr << L"Error generating the code information for model"
	       << std::endl;
  }
  return code;
}
예제 #2
0
std::string generateCodeForModel(CellmlApiObjects* capi,
                                 std::map<std::string, unsigned char>& variableTypes,
                                 std::map<std::string, std::map<unsigned char, int> >& variableIndices,
                                 int numberOfInputs, int numberOfStates)
{
    std::stringstream code;
    std::string codeString;
    if (! (capi && capi->codeInformation))
    {
        std::cerr << "CellML Model Definition::generateCodeForModel: missing model implementation?" << std::endl;
        return "";
    }
    // We need to regenerate the code information to make use of the flagged variables.
    ObjRef<iface::cellml_services::CodeGeneratorBootstrap> cgb = CreateCodeGeneratorBootstrap();
    ObjRef<iface::cellml_services::CodeGenerator> cg = cgb->createCodeGenerator();
    // catch any exceptions from the CellML API
    try
    {
        // annotate the source variables in the model with the code-names based on existing annotations
        for (unsigned int i=0; i < capi->cevas->length(); i++)
        {
            ObjRef<iface::cellml_services::ConnectedVariableSet> cvs = capi->cevas->getVariableSet(i);
            ObjRef<iface::cellml_api::CellMLVariable> sv = cvs->sourceVariable();
            std::string currentId = getVariableUniqueId(sv);
            std::map<std::string, unsigned char>::iterator typeit(variableTypes.find(currentId));
            if (typeit != variableTypes.end())
            {
                std::wstringstream ename;
                // here we assign an array and index based on the "primary" purpose of the variable. Later
                // we will add in secondary purposes.
                unsigned char vType = typeit->second;
                if (vType & csim::StateType)
                {
                    ename << L"CSIM_STATE[" << variableIndices[currentId][csim::StateType] << L"]";
                }
                else if (vType & csim::IndependentType)
                {
                    // do nothing, but stop input and output annotations
                }
                else if (vType & csim::InputType)
                {
                    ename << L"CSIM_INPUT[" << variableIndices[currentId][csim::InputType] << L"]";
                }
                else if (vType & csim::OutputType)
                {
                    ename << L"CSIM_OUTPUT[" << variableIndices[currentId][csim::OutputType] << L"]";
                }
                capi->annotations->setStringAnnotation(sv, L"expression", ename.str());

                if (vType & csim::StateType)
                {
                    ename.str(L"");
                    ename.clear();
                    ename << L"CSIM_RATE[" << variableIndices[currentId][csim::StateType] << L"]";
                    capi->annotations->setStringAnnotation(sv, L"expression_d1", ename.str());
                }
            }
        }
        cg->useCeVAS(capi->cevas);
        cg->useAnnoSet(capi->annotations);
        ObjRef<iface::cellml_services::CodeInformation> cci = cg->generateCode(capi->model);
        std::wstring m = cci->errorMessage();
        if (m != L"")
        {
            std::cerr << "CellML Model Definition::generateCodeForModel: error generating code?" << std::endl;
            return "";
        }
        iface::cellml_services::ModelConstraintLevel mcl = cci->constraintLevel();
        if (mcl == iface::cellml_services::UNDERCONSTRAINED)
        {
            std::cerr << "Model is underconstrained" << std::endl;
            return "";
        }
        else if (mcl == iface::cellml_services::OVERCONSTRAINED)
        {
            std::cerr << "Model is overconstrained" << std::endl;
            return "";
        }
        else if (mcl == iface::cellml_services::UNSUITABLY_CONSTRAINED)
        {
            std::cerr << "Model is unsuitably constrained" << std::endl;
            return "";
        }
        std::cout << "Model is correctly constrained" << std::endl;
        // create the code in the format we know how to handle
        code << "//#include <math.h>\n"
        /* required functions */
             << "double fabs(double x);\n"
             << "double acos(double x);\n"
             << "double acosh(double x);\n"
             << "double atan(double x);\n"
             << "double atanh(double x);\n"
             << "double asin(double x);\n"
             << "double asinh(double x);\n"
             << "double acos(double x);\n"
             << "double acosh(double x);\n"
             << "double asin(double x);\n"
             << "double asinh(double x);\n"
             << "double atan(double x);\n"
             << "double atanh(double x);\n"
             << "double ceil(double x);\n"
             << "double cos(double x);\n"
             << "double cosh(double x);\n"
             << "double tan(double x);\n"
             << "double tanh(double x);\n"
             << "double sin(double x);\n"
             << "double sinh(double x);\n"
             << "double exp(double x);\n"
             << "double floor(double x);\n"
             << "double pow(double x, double y);\n"
             << "double factorial(double x);\n"
             << "double log(double x);\n"
             << "double arbitrary_log(double x, double base);\n"
             << "double gcd_pair(double a, double b);\n"
             << "double lcm_pair(double a, double b);\n"
             << "double gcd_multi(unsigned int size, ...);\n"
             << "double lcm_multi(unsigned int size, ...);\n"
             << "double multi_min(unsigned int size, ...);\n"
             << "double multi_max(unsigned int size, ...);\n"
             << "void NR_MINIMISE(double(*func)"
                "(double VOI, double *C, double *R, double *S, double *A),"
                "double VOI, double *C, double *R, double *S, double *A, "
                "double *V);\n";
        std::wstring frag = cci->functionsString();
        code << ws2s(frag);

        int nAlgebraic = cci->algebraicIndexCount();
        int nConstants = cci->constantIndexCount();

        code << "\n\nvoid csim_rhs_routine(double VOI, double* CSIM_STATE, double* CSIM_RATE, double* CSIM_OUTPUT, "
             << "double* CSIM_INPUT)\n{\n\n"
             << "double DUMMY_ASSIGNMENT;\n"
             << "double CONSTANTS["
             << nConstants
             << "], ALGEBRAIC["
             << nAlgebraic
             << "];\n\n";

        /* initConsts - all variables which aren't state variables but have
         *              an initial_value attribute, and any variables & rates
         *              which follow.
         */
        code << ws2s(cci->initConstsString());

        /* rates      - All rates which are not static.
         */
        code << ws2s(cci->ratesString());

        /* variables  - All variables not computed by initConsts or rates
         *  (i.e., these are not required for the integration of the model and
         *   thus only need to be called for output or presentation or similar
         *   purposes)
         */
        code << ws2s(cci->variablesString());

        // add in the setting of any outputs that are not already defined
        for (unsigned int i=0; i < capi->cevas->length(); i++)
        {
            ObjRef<iface::cellml_services::ConnectedVariableSet> cvs = capi->cevas->getVariableSet(i);
            ObjRef<iface::cellml_api::CellMLVariable> sv = cvs->sourceVariable();
            std::string currentId = getVariableUniqueId(sv);
            std::map<std::string, unsigned char>::iterator typeit(variableTypes.find(currentId));
            if (typeit != variableTypes.end())
            {
                unsigned char vType = typeit->second;
                if (vType & csim::OutputType)
                {
                    if (vType & csim::StateType)
                        code << "CSIM_OUTPUT[" << variableIndices[currentId][csim::OutputType]
                                << "] = CSIM_STATE[" << variableIndices[currentId][csim::StateType]
                                   << "];\n";
                    else if (vType & csim::InputType)
                        code << "CSIM_OUTPUT[" << variableIndices[currentId][csim::OutputType]
                                << "] = CSIM_INPUT[" << variableIndices[currentId][csim::InputType]
                                   << "];\n";
                    else if (vType & csim::IndependentType)
                        code << "CSIM_OUTPUT[" << variableIndices[currentId][csim::OutputType]
                                << "] = VOI;\n";
                }
            }
        }

        // close the subroutine
        code << "\n\n}//csim_rhs_routine()\n\n";

        // and now clear out initialisation of state variables and known variables from
        // the RHS routine.
        codeString = clearCodeAssignments(code.str(), "CSIM_STATE", numberOfStates);
        codeString = clearCodeAssignments(codeString, "CSIM_INPUT", numberOfInputs);

        // and finally create the initialisation routine
        std::stringstream initRoutine;
        initRoutine << "\nvoid csim_initialise_routine(double* CSIM_STATE, double* CSIM_OUTPUT, double* CSIM_INPUT)\n{\n";
        // FIXME: this doesn't need to be in the interface?
        initRoutine << "double CSIM_RATES[" << numberOfStates << "];\n";
        initRoutine << "double CONSTANTS[" << nConstants << "];\n";
        initRoutine << ws2s(cci->initConstsString());
        initRoutine << "\n}\n";

        codeString += initRoutine.str();
    }
    catch (...)
    {
        std::cerr << "CellML Model Definition::generateCodeForModel: something went wrong generating code?" << std::endl;
        return "";
    }
    return codeString;
}