Open kerimoyle opened 5 years ago
For reference, this is the OpenCOR tutorial I'm talking about :)
https://tutorial-on-cellml-opencor-and-pmr.readthedocs.io/en/latest/
I've started by trying to make some template CMake files to build simple examples which use the libcellml library, but I'm confused about how we actually want people to access them (assuming I've got the generation stuff right).
I want to get the basics straight in my own head first so am writing the most bare-bones version of everything. I've turned off all the optional extras (tests, coverage etc), set it to Release, and run make -j
and then make install
in a local directory to install it all.
NB: Confusion about simple stuff is my speciality ... apologies for the dumb questions ...
Question 1: Do we want users to use the same#include <libcellml>
idiom that we use in the tests? In which case, is that a module or a library in CMake parlance? The only way I could get any joy building my wee program was to mention the *.h files by name and specify the include path in the CMakeLists.txt ... which causes problems later (see below).
CMakeLists.txt:
cmake_minimum_required(VERSION 3.0.0)
project(tutorial1 VERSION 0.1.0)
add_executable(tutorial1 main.cpp)
target_include_directories(tutorial1 PUBLIC BEFORE "/Users/kmoy001/libcellml-tutorials/library/include")
main.cpp
#include <iostream>
//#include <libcellml> // Does not work as module or include or library?
#include <libcellml/model.h>
#include <libcellml/parser.h>
int main() {
std::cout << "hello world\n";
libcellml::Parser parser;
}
Question 2: Which version of C++ compiler are we supporting? There was talk recently about upgrades to 19(?) but I'm getting errors like this:
/Users/kmoy001/libcellml-tutorials/library/include/libcellml/entity.h:38:25: error: expected ';' at end of declaration list
[build] Entity(Entity &&rhs) noexcept; /**< Move constructor */
[build] ^
[build] ;
... and warnings like this:
[build] /Users/kmoy001/libcellml-tutorials/library/include/libcellml/enumerations.h:29:6: warning: scoped enumerations are a C++11 extension [-Wc++11-extensions]
[build] enum class Prefix
I've been going through the tutorial from @nickerso above and noticed that (see pg 14) it references units by their symbols as well/instead of the names in the spec table. Or is that just for clarity? If the former, is this something unique to OpenCOR or are we intending to support it in libCellML too? (and @agarny, might need to change spelling of "meter" to be "metre" in any case?)
Not sure when it comes to the tutorial, but my guess would be that it's indeed "just" for clarity. As for OpenCOR and meter
vs. metre
, remember that OpenCOR currently supports CellML 1.0/1.1, and both spellings are allowed. When OpenCOR officially supports CellML 2.0 then only metre
will indeed be allowed.
The CMakeLists.txt above is wrong it should look more like this:
cmake_minimum_required(VERSION 3.10.2)
project(tutorial1 VERSION 0.1.0)
# find_package(libCellML) produces a target 'cellml'
# Use -DlibCellML_DIR=<path-to-directory-containing-libcellml-config.cmake> on command line
# invocations of CMake to allow find_package to find the CellML libraries when
# installed in a non-standard location. Otherwise when using a CMake GUI application
# add/modify the libCellML_DIR variable to the location of libCellML's
# libcellml-config.cmake file.
find_package(libCellML REQUIRED)
add_executable(tutorial1 main.cpp)
target_link_libraries(tutorial1 PUBLIC cellml)
This will fix a few of those problems you are seeing above as the tutorial1
application is not configured properly.
Thanks @hsorby ... this is just the kind of thing I needed :) ... but ... on my Mac there is no option (using ccmake) to give the libCellML_DIR path. There's the LIBCELLML_INSTALL_PREFIX as the closest I can find? Or should I be specifying it somewhere else? I currently have things working (I use the word loosely ... there are lots of segfaults ...) with this:
set(PROJECT_NAME tutorial4)
cmake_minimum_required(VERSION 3.2)
project(${PROJECT_NAME} VERSION 0.1.0 LANGUAGES CXX)
set (CMAKE_CXX_STANDARD 11)
set (PROJECT_SRC
${PROJECT_NAME}.cpp
../utilities/tutorial_utilities.cpp
)
add_executable(${PROJECT_NAME} ${PROJECT_SRC} )
target_include_directories(${PROJECT_NAME} PUBLIC
"/usr/local/include",
"../utilities"
)
target_link_libraries(${PROJECT_NAME} /usr/local/lib/libcellmld.dylib)
set_target_properties(${EXE} PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE)
But you don't have this find_package(libCellML REQUIRED)
so you won't have libCellML_DIR
. Your CMakeLists.txt
looks nothing like mine.
I should have been clearer. I realise they're different ... but when I ran yours I couldn't get the middle instructions to work as there was no option to specify the directory anywhere via the ccmake interface. That was the question: How do I pass in the libCellML_DIR path if I'm using the ccmake gui and it's not one of the options listed?
On Tue, 15 Oct 2019 at 11:36, Hugh Sorby notifications@github.com wrote:
But you don't have this find_package(libCellML REQUIRED) so you won't have libCellML_DIR. Your CMakeLists.txt looks nothing like mine.
— You are receiving this because you were assigned. Reply to this email directly, view it on GitHub https://github.com/cellml/libcellml/issues/425?email_source=notifications&email_token=ALFXCNHAYBTC2TOTQBCFBR3QOWFJDA5CNFSM4I63KSTKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBIDJ6I#issuecomment-542127353, or unsubscribe https://github.com/notifications/unsubscribe-auth/ALFXCNGBUKOUJFE7M5IJZHLQOWFJDANCNFSM4I63KSTA .
When you first try to configure with the find_package(libCellML REQUIRED)
statement it will fail. It will fail because it cannot find libCellML. When CMake configuration fails the libCellML_DIR
variable will become available in the ccmake gui for you to tell CMake which directory libcellml-config.cmake
resides in.
Or you can just pass libCellML_DIR
in from the off and help CMake find libCellML knowing that it can't find it on it's own. That would be done using -DlibCellML_DIR=<path-to-libcellml-config.cmake>
Thanks, will give it a go :)
I think I'm getting there ... but can't find what I've done wrong with this setup (guessing it's in the mathml ... but if so, we really need to catch these somewhere as it's super easy to mess up writing that awful stuff!)
compareVariablesByTypeAndIndex
because variable2
is the nullptr
.The test below passes, the file attached generates a segfault even though the code is the same. tutorial4.cpp.zip
TEST(Generator, tutorial4)
{
// 1.a Create the model instance
libcellml::ModelPtr model = std::make_shared<libcellml::Model>();
model->setName("Tutorial4_FirstOrderModel");
// 1.b Create a component and add it into the model
libcellml::ComponentPtr component = std::make_shared<libcellml::Component>();
component->setName("IonChannel");
model->addComponent(component);
// 2.a Define the mathematics.
std::string mathHeader = "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">";
// dy/dt = alpha_y*(1-y) - beta_y*y
std::string equation1 =
"<apply>\
<eq/>\
<apply>\
<diff/>\
<bvar>\
<ci>t</ci>\
</bvar>\
<ci>y</ci>\
</apply>\
<apply>\
<minus/>\
<apply>\
<times/>\
<ci>alpha_y</ci>\
<apply>\
<minus/>\
<cn cellml:units=\"dimensionless\">1</cn>\
<ci>y</ci>\
</apply>\
</apply>\
<apply>\
<times/>\
<ci>beta_y</ci>\
<ci>y</ci>\
</apply>\
</apply>\
</apply>";
// i_y = g_y*power(y,gamma)*(V-E_y)
std::string equation2 =
"<apply>\
<eq/>\
<ci>i_y</ci>\
<apply>\
<times/>\
<ci>g_y</ci>\
<apply>\
<minus/>\
<ci>V</ci>\
<ci>E_y</ci>\
</apply>\
<apply>\
<power/>\
<ci>y</ci>\
<ci>gamma</ci>\
</apply>\
</apply>\
</apply>";
std::string mathFooter = "</math>";
// 2.b Add the maths to the component. Note that there is only one maths
// string stored, so parts which are appended must create a viable
// MathML2 string when concantenated. To clear any string which is
// already stored, simply call setMath("") with an empty string.
component->setMath(mathHeader);
component->appendMath(equation1);
component->appendMath(equation2);
component->appendMath(mathFooter);
// 3.a,b Declaring the variables, their names, units, and initial conditions
// Note that the names given to variables must be the same as that used
// within the <ci> blocks in the MathML string we created in step 2.a.
libcellml::VariablePtr t = std::make_shared<libcellml::Variable>();
t->setName("t");
t->setUnits("millisecond");
// Note: time is our integration base variable so is not initialised
libcellml::VariablePtr V = std::make_shared<libcellml::Variable>();
V->setName("V");
V->setUnits("millivolt");
V->setInitialValue(0.0);
libcellml::VariablePtr alpha_y = std::make_shared<libcellml::Variable>();
alpha_y->setName("alpha_y");
alpha_y->setUnits("per_millisecond");
alpha_y->setInitialValue(1.0);
libcellml::VariablePtr beta_y = std::make_shared<libcellml::Variable>();
beta_y->setName("beta_y");
beta_y->setUnits("per_millisecond");
beta_y->setInitialValue(2.0);
libcellml::VariablePtr y = std::make_shared<libcellml::Variable>();
y->setName("y");
y->setUnits("dimensionless");
y->setInitialValue(1.0);
libcellml::VariablePtr E_y = std::make_shared<libcellml::Variable>();
E_y->setName("E_y");
E_y->setUnits("millivolt");
E_y->setInitialValue(-85.0);
libcellml::VariablePtr i_y = std::make_shared<libcellml::Variable>();
i_y->setName("i_y");
i_y->setUnits("microA_per_cm2");
// Note that no initial value is needed for this variable as its value
// is defined by equation2
libcellml::VariablePtr g_y = std::make_shared<libcellml::Variable>();
g_y->setName("g_y");
g_y->setUnits("milliS_per_cm2");
g_y->setInitialValue(36.0);
libcellml::VariablePtr gamma = std::make_shared<libcellml::Variable>();
gamma->setName("gamma");
gamma->setUnits("dimensionless");
gamma->setInitialValue(4.0);
// 3.c Adding the variables to the component. Note that Variables are
// added by their pointer (cf. their name)
component->addVariable(t);
component->addVariable(V);
component->addVariable(E_y);
component->addVariable(gamma);
component->addVariable(i_y);
component->addVariable(g_y);
component->addVariable(alpha_y);
component->addVariable(beta_y);
component->addVariable(y);
// 4.a Defining the units of millisecond, millivolt, per_millisecond,
// microA_per_cm2, and milliS_per_cm2. Note that the dimensionless
// units are part of those built-in already, so do not need to be
// defined here.
libcellml::UnitsPtr ms = std::make_shared<libcellml::Units>();
ms->setName("millisecond");
ms->addUnit("second", "milli");
libcellml::UnitsPtr mV = std::make_shared<libcellml::Units>();
mV->setName("millivolt");
mV->addUnit("volt", "milli");
libcellml::UnitsPtr per_ms = std::make_shared<libcellml::Units>();
per_ms->setName("per_millisecond");
per_ms->addUnit("millisecond", -1.0);
libcellml::UnitsPtr microA_per_cm2 = std::make_shared<libcellml::Units>();
microA_per_cm2->setName("microA_per_cm2");
microA_per_cm2->addUnit("ampere", "micro");
microA_per_cm2->addUnit("metre", "centi", -2.0);
libcellml::UnitsPtr mS_per_cm2 = std::make_shared<libcellml::Units>();
mS_per_cm2->setName("milliS_per_cm2");
mS_per_cm2->addUnit("siemens", "milli");
mS_per_cm2->addUnit("metre", "centi", -2.0);
// 4.b Add these units into the model
model->addUnits(ms);
model->addUnits(mV);
model->addUnits(per_ms);
model->addUnits(microA_per_cm2);
model->addUnits(mS_per_cm2);
// 4.c Validate the final arrangement. No errors are expected at this stage.
libcellml::Validator validator;
validator.validateModel(model);
printErrors(validator);
// 5.a Create a Generator instance. By default the options set in the
// generator constructor are:
// - profile() return "C" (cf "PYTHON")
// - modelType() returns "ODE"
libcellml::Generator generator;
generator.processModel(model);
// 5.b Check whether the generator has encountered any errors
std::cout << "The generator returned " << generator.errorCount() << " errors!" << std::endl;
for (size_t e = 0; e < generator.errorCount(); e++) {
std::cout << generator.error(e)->description() << std::endl;
}
EXPECT_EQ(size_t(0), generator.errorCount());
}
Have managed to create an isolated test which shows the segfault here not got any further than confirming the problem as a test.
This branch has the same test twice once in generator/generator.cpp
and isolated/generator.cpp
. The first instance of the test in generator/generator.cpp
doesn't segfault while the second isolated/generator.cpp
does!
Thanks Hugh. I've found I'm getting a LOT more segfaults when using the library externally ... used to not get any just from the tests. It must be that magical compiler switch GENERATE_SEGFAULTS which is set to TRUE somewhere ...
Before releasing libcellml out into the big wide world we need to have some user tutorials covering:
@nickerso suggested I follow the use cases addressed by the OpenCOR tutorials - if anyone has any suggestions for what else should be included, please let me know.