QStringList CellmlFileRuntime::componentHierarchy(iface::cellml_api::CellMLElement *pElement) { // Make sure that we have a given element if (!pElement) return QStringList(); // Try to retrieve the component that owns the given element, unless the // given element is a component itself (which will be the case when we come // here through recursion) ObjRef<iface::cellml_api::CellMLComponent> component = QueryInterface(pElement); ObjRef<iface::cellml_api::CellMLElement> parent = pElement->parentElement(); ObjRef<iface::cellml_api::CellMLComponent> parentComponent = QueryInterface(parent); if (!component && !parentComponent) { // The element isn't a component and neither is its parent, so it // doesn't have a hierarchy return QStringList(); } // Check whether this is an imported component and, if so, retrieve its // imported name QString componentName = QString::fromStdWString(component?component->name():parentComponent->name()); ObjRef<iface::cellml_api::CellMLElement> componentParent = component?component->parentElement():parentComponent->parentElement(); ObjRef<iface::cellml_api::CellMLElement> componentParentParent = componentParent->parentElement(); if (componentParentParent) { // The given element comes from or is an imported component, so go // through our different imported components and look for the one we are // after ObjRef<iface::cellml_api::CellMLImport> import = QueryInterface(componentParentParent); ObjRef<iface::cellml_api::ImportComponentSet> importComponents = import->components(); ObjRef<iface::cellml_api::ImportComponentIterator> importComponentsIter = importComponents->iterateImportComponents(); for (ObjRef<iface::cellml_api::ImportComponent> importComponent = importComponentsIter->nextImportComponent(); importComponent; importComponent = importComponentsIter->nextImportComponent()) { if (!componentName.compare(QString::fromStdWString(importComponent->componentRef()))) { // This is the imported component we are after, so retrieve its // imported name componentName = QString::fromStdWString(importComponent->name()); break; } } } // Recursively retrieve the component hierarchy of the given element's // encapsulation parent, if any ObjRef<iface::cellml_api::CellMLComponent> componentEncapsulationParent = component?component->encapsulationParent():parentComponent->encapsulationParent(); return componentHierarchy(componentEncapsulationParent) << componentName; }
std::wstring CompactorReport::getReport() const { std::wstringstream report; report << L"Model Compaction Report\n" << L"=======================\n\n"; if (! mErrorMessage.empty()) report << L"Error message: " << mErrorMessage << L"\n\n"; std::wstring indent = L""; if (mVariableForCompaction.size() > 0) { report << L"Some variables have not been compacted.\n" << L"Uncompacted variables are given below.\n\n"; for (size_t i=0; i<mVariableForCompaction.size(); ++i) { ObjRef<iface::cellml_api::CellMLVariable> variable = mVariableForCompaction[i].first; ObjRef<iface::cellml_api::CellMLVariable> srcVariable = mVariableForCompaction[i].second; std::wstring modelUri = variable->modelElement()->base_uri()->asText(); std::wstring srcModelUri = srcVariable->modelElement()->base_uri()->asText(); for (size_t j=0;j<i;++j) indent += L"\t"; report << indent << L"Compaction requested for variable: " << modelUri << L" # " << variable->componentName() << L" / " << variable->name() << L";\n" << indent << L"with the actual source variable being: " << srcModelUri << L" # " << srcVariable->componentName() << L" / " << srcVariable->name() << L"\n"; } } report.flush(); return report.str(); }
void Tests::basicTests() { // Some very basic tests to make sure that we have access to the CellML API // Get a bootstrap object and its model loader ObjRef<iface::cellml_api::CellMLBootstrap> cellmlBootstrap = CreateCellMLBootstrap(); ObjRef<iface::cellml_api::DOMModelLoader> modelLoader = cellmlBootstrap->modelLoader(); QVERIFY(cellmlBootstrap); QVERIFY(modelLoader); // Create a CellML 1.0 model ObjRef<iface::cellml_api::Model> cellml10Model = cellmlBootstrap->createModel(L"1.0"); QVERIFY(cellml10Model); // Create a CellML 1.1 model ObjRef<iface::cellml_api::Model> cellml11Model = cellmlBootstrap->createModel(L"1.1"); QVERIFY(cellml11Model); // Create an invalid CellML model ObjRef<iface::cellml_api::Model> invalidCellmlModel; try { invalidCellmlModel = cellmlBootstrap->createModel(L"xxx"); } catch (...) { QVERIFY(!invalidCellmlModel); } // Load an existing model ObjRef<iface::cellml_api::Model> existingModel = modelLoader->loadFromURL(QUrl::fromPercentEncoding(QUrl::fromLocalFile(QFileInfo(OpenCOR::fileName("models/noble_model_1962.cellml")).canonicalFilePath()).toEncoded()).toStdWString()); QVERIFY(existingModel); QCOMPARE(QString::fromStdWString(existingModel->name()), QString("noble_model_1962")); QCOMPARE(QString::fromStdWString(existingModel->cellmlVersion()), QString("1.0")); // Load a non-existing model ObjRef<iface::cellml_api::Model> nonExistingModel; try { nonExistingModel = modelLoader->loadFromURL(L"xxx"); } catch (...) { QVERIFY(!nonExistingModel); } }
void WriteCode(iface::cellml_services::CodeInformation* cci, uint32_t useida) { iface::cellml_services::ModelConstraintLevel mcl = cci->constraintLevel(); if (mcl == iface::cellml_services::UNDERCONSTRAINED) { ObjRef<iface::cellml_services::ComputationTarget> ctMissingIV(cci->missingInitial()); if (ctMissingIV != NULL) { ObjRef<iface::cellml_api::CellMLVariable> v = ctMissingIV->variable(); std::wstring n = v->name(); std::wstring c = v->componentName(); std::wstring str; uint32_t deg = ctMissingIV->degree(); if (deg != 0) { str += L"d^"; wchar_t buf[20]; any_swprintf(buf, 20, L"%u", deg); str += buf; str += L"/dt^"; str += buf; str += L" "; } str += n; str += L" (in "; str += c; str += L")\n"; printf("/* Model is underconstrained due to missing initial_value on %S\n", str.c_str()); } else { printf("/* Model is underconstrained.\n" " * List of undefined targets follows...\n"); iface::cellml_services::ComputationTargetIterator* cti = cci->iterateTargets(); iface::cellml_services::ComputationTarget* ct; std::vector<std::wstring> messages; while (true) { ct = cti->nextComputationTarget(); if (ct == NULL) break; if (ct->type() != iface::cellml_services::FLOATING) { ct->release_ref(); continue; } iface::cellml_api::CellMLVariable* v = ct->variable(); std::wstring n = v->name(); std::wstring c = v->componentName(); std::wstring str = L" * * "; uint32_t deg = ct->degree(); if (deg != 0) { str += L"d^"; wchar_t buf[20]; any_swprintf(buf, 20, L"%u", deg); str += buf; str += L"/dt^"; str += buf; str += L" "; } str += n; str += L" (in "; str += c; str += L")\n"; messages.push_back(str); v->release_ref(); ct->release_ref(); } cti->release_ref(); // Sort the messages... std::sort(messages.begin(), messages.end()); std::vector<std::wstring>::iterator msgi; for (msgi = messages.begin(); msgi != messages.end(); msgi++) printf("%S", (*msgi).c_str()); printf(" */\n"); } return; } else if (mcl == iface::cellml_services::OVERCONSTRAINED) { printf("/* Model is overconstrained.\n" " * List variables defined at time of error follows...\n"); iface::cellml_services::ComputationTargetIterator* cti = cci->iterateTargets(); iface::cellml_services::ComputationTarget* ct; std::vector<std::wstring> messages; while (true) { ct = cti->nextComputationTarget(); if (ct == NULL) break; if (ct->type() == iface::cellml_services::FLOATING) { ct->release_ref(); continue; } iface::cellml_api::CellMLVariable* v = ct->variable(); std::wstring n = v->name(); std::wstring str = L" * * "; uint32_t deg = ct->degree(); if (deg != 0) { str += L"d^"; wchar_t buf[20]; any_swprintf(buf, 20, L"%u", deg); str += buf; str += L"/dt^"; str += buf; str += L" "; } str += n; str += L"\n"; messages.push_back(str); v->release_ref(); ct->release_ref(); } cti->release_ref(); // Sort the messages... std::sort(messages.begin(), messages.end()); std::vector<std::wstring>::iterator msgi; for (msgi = messages.begin(); msgi != messages.end(); msgi++) printf("%S", (*msgi).c_str()); // Get flagged equations... iface::mathml_dom::MathMLNodeList* mnl = cci->flaggedEquations(); printf(" * Extraneous equation was:\n"); iface::dom::Node* n = mnl->item(0); mnl->release_ref(); iface::dom::Element* el = reinterpret_cast<iface::dom::Element*>(n->query_interface("dom::Element")); n->release_ref(); std::wstring cmeta = el->getAttribute(L"id"); if (cmeta == L"") printf(" * <equation with no cmeta ID>\n"); else printf(" * %S\n", cmeta.c_str()); n = el->parentNode(); el->release_ref(); if (n != NULL) { el = reinterpret_cast<iface::dom::Element*> (n->query_interface("dom::Element")); n->release_ref(); cmeta = el->getAttribute(L"id"); if (cmeta == L"") printf(" * in <math with no cmeta ID>\n"); else printf(" * in math with cmeta:id %S\n", cmeta.c_str()); el->release_ref(); } printf(" */\n"); return; } else if (mcl == iface::cellml_services::UNSUITABLY_CONSTRAINED) { printf("/* Model is unsuitably constrained (i.e. would need capabilities" " beyond those of the CCGS to solve).\n" " * The status of variables at time of error follows...\n"); iface::cellml_services::ComputationTargetIterator* cti = cci->iterateTargets(); iface::cellml_services::ComputationTarget* ct; std::vector<std::wstring> messages; while (true) { ct = cti->nextComputationTarget(); if (ct == NULL) break; std::wstring str = L" * * "; if (ct->type() == iface::cellml_services::FLOATING) str += L" Undefined: "; else str += L" Defined: "; uint32_t deg = ct->degree(); if (deg != 0) { str += L"d^"; wchar_t buf[20]; any_swprintf(buf, 20, L"%u", deg); str += buf; str += L"/dt^"; str += buf; str += L" "; } iface::cellml_api::CellMLVariable* v = ct->variable(); std::wstring n = v->name(); str += n; str += L"\n"; messages.push_back(str); v->release_ref(); ct->release_ref(); } cti->release_ref(); // Sort the messages... std::sort(messages.begin(), messages.end()); std::vector<std::wstring>::iterator msgi; for (msgi = messages.begin(); msgi != messages.end(); msgi++) printf("%S", (*msgi).c_str()); printf(" */\n"); return; } printf("/* Model is correctly constrained.\n"); iface::mathml_dom::MathMLNodeList* mnl = cci->flaggedEquations(); uint32_t i, l = mnl->length(); if (l == 0) printf(" * No equations needed Newton-Raphson evaluation.\n"); else printf(" * The following equations needed Newton-Raphson evaluation:\n"); std::vector<std::wstring> messages; for (i = 0; i < l; i++) { iface::dom::Node* n = mnl->item(i); iface::dom::Element* el = reinterpret_cast<iface::dom::Element*>(n->query_interface("dom::Element")); n->release_ref(); std::wstring cmeta = el->getAttribute(L"id"); std::wstring str; if (cmeta == L"") str += L" * <equation with no cmeta ID>\n"; else { str += L" * "; str += cmeta; str += L"\n"; } n = el->parentNode(); el->release_ref(); el = reinterpret_cast<iface::dom::Element*> (n->query_interface("dom::Element")); n->release_ref(); cmeta = el->getAttribute(L"id"); if (cmeta == L"") str += L" * in <math with no cmeta ID>\n"; else { str += L" * in math with cmeta:id "; str += cmeta; str += L"\n"; } el->release_ref(); messages.push_back(str); } mnl->release_ref(); // Sort the messages... std::sort(messages.begin(), messages.end()); std::vector<std::wstring>::iterator msgi; for (msgi = messages.begin(); msgi != messages.end(); msgi++) printf("%S", (*msgi).c_str()); printf(" * The rate and state arrays need %u entries.\n", cci->rateIndexCount()); printf(" * The algebraic variables array needs %u entries.\n", cci->algebraicIndexCount()); printf(" * The constant array needs %u entries.\n", cci->constantIndexCount()); printf(" * Variable storage is as follows:\n"); messages.clear(); iface::cellml_services::ComputationTargetIterator* cti = cci->iterateTargets(); while (true) { iface::cellml_services::ComputationTarget* ct = cti->nextComputationTarget(); if (ct == NULL) break; iface::cellml_api::CellMLVariable* v = ct->variable(); iface::cellml_api::CellMLElement* el = v->parentElement(); iface::cellml_api::CellMLComponent* c = reinterpret_cast<iface::cellml_api::CellMLComponent*> (el->query_interface("cellml_api::CellMLComponent")); el->release_ref(); std::wstring str; std::wstring vn = v->name(), cn = c->name(); str += L" * * Target "; uint32_t deg = ct->degree(); if (deg != 0) { str += L"d^"; wchar_t buf[20]; any_swprintf(buf, 20, L"%u", deg); str += buf; str += L"/dt^"; str += buf; str += L" "; } str += vn; str += L" in component "; str += cn; str += L"\n"; c->release_ref(); v->release_ref(); str += L" * * * Variable type: "; str += TypeToString(ct->type()); str += L"\n * * * Variable index: "; wchar_t buf[40]; any_swprintf(buf, 40, L"%u\n", ct->assignedIndex()); str += buf; str += L" * * * Variable storage: "; std::wstring vsn = ct->name(); str += vsn; str += '\n'; ct->release_ref(); messages.push_back(str); } cti->release_ref(); // Sort the messages... std::sort(messages.begin(), messages.end()); for (msgi = messages.begin(); msgi != messages.end(); msgi++) printf("%S", (*msgi).c_str()); printf(" */\n"); std::wstring frag = cci->functionsString(); printf("%S", frag.c_str()); // Now start the code... frag = cci->initConstsString(); printf("void SetupFixedConstants(double* CONSTANTS, double* RATES, double* STATES)\n{\n%S}\n", frag.c_str()); frag = cci->variablesString(); printf("void EvaluateVariables(double VOI, double* CONSTANTS, double* RATES, double* STATES, double* ALGEBRAIC)\n" "{\n%S}\n", frag.c_str()); if (useida) { iface::cellml_services::IDACodeInformation* icci = reinterpret_cast<iface::cellml_services::IDACodeInformation*>(cci->query_interface("cellml_services::IDACodeInformation")); frag = icci->essentialVariablesString(); printf("void EvaluateEssentialVariables(double VOI, double* CONSTANTS, double* RATES, double* STATES, double* ALGEBRAIC)\n" "{\n%S}\n", frag.c_str()); frag = cci->ratesString(); printf("void ComputeResiduals(double VOI, double* STATES, double* RATES, double* CONSTANTS, " "double* ALGEBRAIC)\n" "{\n%S}\n", frag.c_str()); frag = icci->stateInformationString(); printf("void SetupStateInfo(double * SI)\n{\n%S}\n", frag.c_str()); frag = icci->rootInformationString(); printf("void RootInformation()\n{\n%S}\n", frag.c_str()); icci->release_ref(); } else { frag = cci->ratesString(); printf("void ComputeRates(double VOI, double* STATES, double* RATES, double* CONSTANTS, " "double* ALGEBRAIC)\n" "{\n%S}\n", frag.c_str()); } }
QStringList CellmlFileRuntime::componentHierarchy(iface::cellml_api::CellMLElement *pElement) { // Make sure that we have a given element if (!pElement) return QStringList(); // Try to retrieve the component that owns the given element, unless the // given element is a component itself (which will be the case when we come // here through recursion) ObjRef<iface::cellml_api::CellMLComponent> component = QueryInterface(pElement); ObjRef<iface::cellml_api::CellMLElement> parent = pElement->parentElement(); ObjRef<iface::cellml_api::CellMLComponent> parentComponent = QueryInterface(parent); if (!component && !parentComponent) { // The element isn't a component and neither is its parent, so it // doesn't have a hierarchy return QStringList(); } // Recursively retrieve the component hierarchy of the given element's // encapsulation parent, if any ObjRef<iface::cellml_api::CellMLComponent> componentEncapsulationParent = component?component->encapsulationParent():parentComponent->encapsulationParent(); return componentHierarchy(componentEncapsulationParent) << QString::fromStdWString(component?component->name():parentComponent->name()); }
void CellmlFileRuntime::update() { // Reset the runtime's properties reset(true, true); // Check that the model is either a 'simple' ODE model or a DAE model // Note #1: we don't check whether a model is valid, since all we want is to // update its runtime (which has nothing to do with editing or even // validating a model), so if it can be done then great otherwise // tough luck (so to speak)... // Note #2: in order to do so, we need to get a 'normal' code generator (as // opposed to an IDA, i.e. DAE, code generator) since if the model // is correctly constrained, then we can check whether some of its // equations were flagged as needing a Newton-Raphson evaluation, // in which case we would be dealing with a DAE model... // Note #3: ideally, there would be a more convenient way to determine the // type of a model, but there isn't... iface::cellml_api::Model *model = mCellmlFile->model(); if (!model) return; // Retrieve the model's type // Note: this can be done by checking whether some equations were flagged // as needing a Newton-Raphson evaluation... retrieveOdeCodeInformation(model); if (!mOdeCodeInformation) return; ObjRef<iface::mathml_dom::MathMLNodeList> flaggedEquations = mOdeCodeInformation->flaggedEquations(); mModelType = flaggedEquations->length()?CellmlFileRuntime::Dae:CellmlFileRuntime::Ode; // If the model is of DAE type, then we don't want the ODE-specific code // information, but the DAE-specific code one ObjRef<iface::cellml_services::CodeInformation> genericCodeInformation; if (mModelType == CellmlFileRuntime::Ode) { genericCodeInformation = mOdeCodeInformation; } else { retrieveDaeCodeInformation(model); if (!mDaeCodeInformation) return; genericCodeInformation = mDaeCodeInformation; } // Retrieve the number of constants, states/rates, algebraic and conditional // variables in the model // Note: this is to avoid having to go through the ODE/DAE code information // an unnecessary number of times when we want to retrieve either of // those numbers (e.g. see // SingleCellViewSimulationResults::addPoint())... if (mModelType == CellmlFileRuntime::Ode) { mConstantsCount = mOdeCodeInformation->constantIndexCount(); mStatesRatesCount = mOdeCodeInformation->rateIndexCount(); mAlgebraicCount = mOdeCodeInformation->algebraicIndexCount(); mCondVarCount = 0; } else { mConstantsCount = mDaeCodeInformation->constantIndexCount(); mStatesRatesCount = mDaeCodeInformation->rateIndexCount(); mAlgebraicCount = mDaeCodeInformation->algebraicIndexCount(); mCondVarCount = mDaeCodeInformation->conditionVariableCount(); } // Retrieve all the parameters and sort them by component/variable name ObjRef<iface::cellml_services::ComputationTargetIterator> computationTargetIter = genericCodeInformation->iterateTargets(); for (ObjRef<iface::cellml_services::ComputationTarget> computationTarget = computationTargetIter->nextComputationTarget(); computationTarget; computationTarget = computationTargetIter->nextComputationTarget()) { // Determine the type of the parameter ObjRef<iface::cellml_api::CellMLVariable> variable = computationTarget->variable(); CellmlFileRuntimeParameter::ParameterType parameterType; switch (computationTarget->type()) { case iface::cellml_services::VARIABLE_OF_INTEGRATION: parameterType = CellmlFileRuntimeParameter::Voi; break; case iface::cellml_services::CONSTANT: // We are dealing with a constant, but the question is whether that // constant is a 'proper' constant, a 'computed' constant or even a // rate, and this can be determined by checking whether the computed // target has an initial value or even a degree // Note: a state variable that is initialised using the initial // value of another variable will have its rate considered as // a constant. However, when it comes to the GUI, we really // want it to be seen as a rate hence we check for the degree // of the computed target... if (QString::fromStdWString(variable->initialValue()).isEmpty()) { // The computed target doesn't have an initial value, so it must // be a 'computed' constant parameterType = CellmlFileRuntimeParameter::ComputedConstant; } else if (computationTarget->degree()) { // The computed target has a degree, so it is effectively a rate parameterType = CellmlFileRuntimeParameter::Rate; } else { // The computed target has an initial value, so it must be a // 'proper' constant parameterType = CellmlFileRuntimeParameter::Constant; } break; case iface::cellml_services::STATE_VARIABLE: case iface::cellml_services::PSEUDOSTATE_VARIABLE: parameterType = CellmlFileRuntimeParameter::State; break; case iface::cellml_services::ALGEBRAIC: // We are dealing with either a 'proper' algebraic variable or a // rate variable // Note: if the variable's degree is equal to zero, then we are // dealing with a 'proper' algebraic variable otherwise we // are dealing with a rate variable... if (computationTarget->degree()) parameterType = CellmlFileRuntimeParameter::Rate; else parameterType = CellmlFileRuntimeParameter::Algebraic; break; case iface::cellml_services::FLOATING: parameterType = CellmlFileRuntimeParameter::Floating; break; case iface::cellml_services::LOCALLY_BOUND: parameterType = CellmlFileRuntimeParameter::LocallyBound; break; } // Keep track of the parameter, should its type be of interest if ( (parameterType != CellmlFileRuntimeParameter::Floating) && (parameterType != CellmlFileRuntimeParameter::LocallyBound)) { CellmlFileRuntimeParameter *parameter = new CellmlFileRuntimeParameter(QString::fromStdWString(variable->name()), computationTarget->degree(), QString::fromStdWString(variable->unitsName()), componentHierarchy(variable), parameterType, computationTarget->assignedIndex()); if (parameterType == CellmlFileRuntimeParameter::Voi) mVariableOfIntegration = parameter; mParameters.append(parameter); } } std::sort(mParameters.begin(), mParameters.end(), sortParameters); // Generate the model code, after having prepended to it all the external // functions that may, or not, be needed // Note: indeed, we cannot include header files since we don't (and don't // want in order to avoid complications) deploy them with OpenCOR. So, // instead, we must declare as external functions all the functions // that we would normally use through header files... QString modelCode = "extern double fabs(double);\n" "\n" "extern double exp(double);\n" "extern double log(double);\n" "\n" "extern double ceil(double);\n" "extern double floor(double);\n" "\n" "extern double factorial(double);\n" "\n" "extern double sin(double);\n" "extern double cos(double);\n" "extern double tan(double);\n" "extern double sinh(double);\n" "extern double cosh(double);\n" "extern double tanh(double);\n" "extern double asin(double);\n" "extern double acos(double);\n" "extern double atan(double);\n" "extern double asinh(double);\n" "extern double acosh(double);\n" "extern double atanh(double);\n" "\n" "extern double arbitrary_log(double, double);\n" "\n" "extern double pow(double, double);\n" "\n" "extern double gcd_multi(int, ...);\n" "extern double lcm_multi(int, ...);\n" "extern double multi_max(int, ...);\n" "extern double multi_min(int, ...);\n" "\n"; QString functionsString = QString::fromStdWString(genericCodeInformation->functionsString()); if (!functionsString.isEmpty()) { // We will need to solve at least one NLA system mAtLeastOneNlaSystem = true; modelCode += "struct rootfind_info\n" "{\n" " double aVOI;\n" "\n" " double *aCONSTANTS;\n" " double *aRATES;\n" " double *aSTATES;\n" " double *aALGEBRAIC;\n" "\n" " int *aPRET;\n" "};\n" "\n" "extern void doNonLinearSolve(char *, void (*)(double *, double *, void*), double *, int *, int, void *);\n" "\n" +functionsString.replace("do_nonlinearsolve(", QString("doNonLinearSolve(\"%1\", ").arg(address())) +"\n"; // Note: we rename do_nonlinearsolve() to doNonLinearSolve() because // CellML's CIS service already defines do_nonlinearsolve(), yet // we want to use our own non-linear solve routine defined in our // Compiler plugin. Also, we add a new parameter to all our calls // to doNonLinearSolve() so that doNonLinearSolve() can retrieve // the correct instance of our NLA solver... } // Retrieve the body of the function that initialises constants and extract // the statements that are related to computed variables (since we want to // be able to recompute those whenever the user modifies a parameter) // Note: ideally, we wouldn't have to do that, but the CellML API doesn't // distinguish between 'proper' and 'computed' constants... // (See https://tracker.physiomeproject.org/show_bug.cgi?id=3499) static const QRegularExpression InitializationStatementRegEx = QRegularExpression("^(CONSTANTS|RATES|STATES)\\[\\d*\\] = [+-]?\\d*\\.?\\d+([eE][+-]?\\d+)?;$"); QStringList initConstsList = QString::fromStdWString(genericCodeInformation->initConstsString()).split("\r\n"); QString initConsts = QString(); QString compCompConsts = QString(); foreach (const QString &initConst, initConstsList) { // Add the statement either to our list of 'proper' constants or // 'computed' constants if (InitializationStatementRegEx.match(initConst).hasMatch()) { // We are dealing with a 'proper' constant (or a rate or a state) if (!initConsts.isEmpty()) initConsts += "\n"; initConsts += initConst; } else { // We are dealing with a 'computed' constant if (!compCompConsts.isEmpty()) compCompConsts += "\n"; compCompConsts += initConst; } } modelCode += functionCode("int initializeConstants(double *CONSTANTS, double *RATES, double *STATES)", initConsts, true); modelCode += "\n"; modelCode += functionCode("int computeComputedConstants(double *CONSTANTS, double *RATES, double *STATES)", compCompConsts, true); modelCode += "\n"; // Retrieve the body of the remaining functions if (mModelType == CellmlFileRuntime::Ode) { modelCode += functionCode("int computeOdeRates(double VOI, double *CONSTANTS, double *RATES, double *STATES, double *ALGEBRAIC)", QString::fromStdWString(mOdeCodeInformation->ratesString())); modelCode += "\n"; modelCode += functionCode("int computeOdeVariables(double VOI, double *CONSTANTS, double *RATES, double *STATES, double *ALGEBRAIC)", QString::fromStdWString(genericCodeInformation->variablesString())); } else { modelCode += functionCode("int computeDaeEssentialVariables(double VOI, double *CONSTANTS, double *RATES, double *OLDRATES, double *STATES, double *OLDSTATES, double *ALGEBRAIC, double *CONDVAR)", QString::fromStdWString(mDaeCodeInformation->essentialVariablesString())); modelCode += "\n"; modelCode += functionCode("int computeDaeResiduals(double VOI, double *CONSTANTS, double *RATES, double *OLDRATES, double *STATES, double *OLDSTATES, double *ALGEBRAIC, double *CONDVAR, double *resid)", QString::fromStdWString(mDaeCodeInformation->ratesString())); modelCode += "\n"; modelCode += functionCode("int computeDaeRootInformation(double VOI, double *CONSTANTS, double *RATES, double *OLDRATES, double *STATES, double *OLDSTATES, double *ALGEBRAIC, double *CONDVAR)", QString::fromStdWString(mDaeCodeInformation->rootInformationString())); modelCode += functionCode("int computeDaeStateInformation(double *SI)", QString::fromStdWString(mDaeCodeInformation->stateInformationString())); modelCode += "\n"; modelCode += functionCode("int computeDaeVariables(double VOI, double *CONSTANTS, double *RATES, double *STATES, double *ALGEBRAIC, double *CONDVAR)", QString::fromStdWString(genericCodeInformation->variablesString())); } // Check whether the model code contains a definite integral, otherwise // compute it and check that everything went fine if (modelCode.contains("defint(func")) { mIssues << CellmlFileIssue(CellmlFileIssue::Error, QObject::tr("definite integrals are not yet supported")); } else if (!mCompilerEngine->compileCode(modelCode)) { mIssues << CellmlFileIssue(CellmlFileIssue::Error, QString("%1").arg(mCompilerEngine->error())); } // Keep track of the ODE/DAE functions, but only if no issues were reported if (mIssues.count()) { reset(true, false); } else { // Add the symbol of any required external function, if any if (mAtLeastOneNlaSystem) llvm::sys::DynamicLibrary::AddSymbol("doNonLinearSolve", (void *) (intptr_t) doNonLinearSolve); // Retrieve the ODE/DAE functions mInitializeConstants = (InitializeConstantsFunction) (intptr_t) mCompilerEngine->getFunction("initializeConstants"); mComputeComputedConstants = (ComputeComputedConstantsFunction) (intptr_t) mCompilerEngine->getFunction("computeComputedConstants"); if (mModelType == CellmlFileRuntime::Ode) { mComputeOdeRates = (ComputeOdeRatesFunction) (intptr_t) mCompilerEngine->getFunction("computeOdeRates"); mComputeOdeVariables = (ComputeOdeVariablesFunction) (intptr_t) mCompilerEngine->getFunction("computeOdeVariables"); } else { mComputeDaeEssentialVariables = (ComputeDaeEssentialVariablesFunction) (intptr_t) mCompilerEngine->getFunction("computeDaeEssentialVariables"); mComputeDaeResiduals = (ComputeDaeResidualsFunction) (intptr_t) mCompilerEngine->getFunction("computeDaeResiduals"); mComputeDaeRootInformation = (ComputeDaeRootInformationFunction) (intptr_t) mCompilerEngine->getFunction("computeDaeRootInformation"); mComputeDaeStateInformation = (ComputeDaeStateInformationFunction) (intptr_t) mCompilerEngine->getFunction("computeDaeStateInformation"); mComputeDaeVariables = (ComputeDaeVariablesFunction) (intptr_t) mCompilerEngine->getFunction("computeDaeVariables"); } // Make sure that we managed to retrieve all the ODE/DAE functions bool functionsOk = mInitializeConstants && mComputeComputedConstants; if (mModelType == CellmlFileRuntime::Ode) { functionsOk = functionsOk && mComputeOdeRates && mComputeOdeVariables; } else { functionsOk = functionsOk && mComputeDaeEssentialVariables && mComputeDaeResiduals && mComputeDaeRootInformation && mComputeDaeStateInformation && mComputeDaeVariables; } if (!functionsOk) { mIssues << CellmlFileIssue(CellmlFileIssue::Error, QObject::tr("an unexpected problem occurred while trying to retrieve the model functions")); reset(true, false); } } }