diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d522c7d5c15fb7f4bf3cd4e45924ae57a9bd750..abce9d7acbb240b6c2d6e0e49a0c273d4683c9b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,7 +152,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) # in the same CMakeLists.txt in which the target is defined. # Interface library `arbor-config-defs` collects configure-time defines -# for arbor, arborenv, arborio and arbornml, of the form ARB_HAVE_XXX. These +# for arbor, arborenv, arborio, of the form ARB_HAVE_XXX. These # defines should _not_ be used in any installed public headers. add_library(arbor-config-defs INTERFACE) @@ -171,13 +171,6 @@ add_library(arborenv-private-deps INTERFACE) target_link_libraries(arborenv-private-deps INTERFACE arbor-config-defs) install(TARGETS arborenv-private-deps EXPORT arbor-targets) -# Interface library `arbornml-private-deps` collects dependencies, options etc. -# for the arbornml library. - -add_library(arbornml-private-deps INTERFACE) -target_link_libraries(arbornml-private-deps INTERFACE arbor-config-defs) -install(TARGETS arbornml-private-deps EXPORT arbor-targets) - # Interface library `arborio-private-deps` collects dependencies, options etc. # for the arborio library. @@ -192,12 +185,12 @@ install(TARGETS arborio-private-deps EXPORT arbor-targets) add_library(arbor-public-deps INTERFACE) install(TARGETS arbor-public-deps EXPORT arbor-targets) -# Interface library `arbornml-public-deps` collects requirements for the -# users of the arbornml library (e.g. xml libs) that will become part -# of arbornml's PUBLIC interface. +# Interface library `arborio-public-deps` collects requirements for the +# users of the arborio library (e.g. xml libs) that will become part +# of arborio's PUBLIC interface. -add_library(arbornml-public-deps INTERFACE) -install(TARGETS arbornml-public-deps EXPORT arbornml-targets) +add_library(arborio-public-deps INTERFACE) +install(TARGETS arborio-public-deps EXPORT arborio-targets) # External libraries in `ext` sub-directory: json, tinyopt and randon123. # Creates interface libraries `ext-json`, `ext-tinyopt` and `ext-random123` @@ -218,7 +211,7 @@ set(arbor_export_dependencies) # Keep track of which 'components' of arbor are included (this is # currently just 'MPI' support and 'neuroml' for NeuroML support in -# libarbornml.) +# libarborio.) set(arbor_supported_components) @@ -439,11 +432,6 @@ add_subdirectory(arborenv) # arborio, arborio-public-headers: add_subdirectory(arborio) -# arbornml, arbornml-public-headers: -if(ARB_WITH_NEUROML) - add_subdirectory(arbornml) -endif() - # unit, unit-mpi, unit-local, unit-modcc add_subdirectory(test) @@ -493,7 +481,6 @@ endif() set(arbor_override_import_lang) set(arbor_add_import_libs) set(arborenv_add_import_libs) -set(arbornml_add_import_libs) set(arborio_add_import_libs) if(ARB_WITH_GPU) diff --git a/arbor/include/CMakeLists.txt b/arbor/include/CMakeLists.txt index ebc48ee4f0b0b2bf97adb790e30175fb15a675da..b61d4a6d45ed4be0176ce18213511a1b00bef474 100644 --- a/arbor/include/CMakeLists.txt +++ b/arbor/include/CMakeLists.txt @@ -38,6 +38,10 @@ if(ARB_WITH_GPU) # define ARB_GPU_ENABLED in version.hpp list(APPEND arb_features GPU) endif() +if(ARB_WITH_NEUROML) + # define ARB_NEUROML_ENABLED in version.hpp + list(APPEND arb_features NEUROML) +endif() if(ARB_WITH_PROFILING) # define ARB_PROFILE_ENABLED in version.hpp list(APPEND arb_features PROFILE) diff --git a/arborio/CMakeLists.txt b/arborio/CMakeLists.txt index e07ce90d4ad50dcedece2352043487c5aab618d6..4cc0e2a3ac6512f49928d191048d81d2a5974569 100644 --- a/arborio/CMakeLists.txt +++ b/arborio/CMakeLists.txt @@ -1,6 +1,16 @@ set(arborio-sources swcio.cpp ) +if(ARB_WITH_NEUROML) + list(APPEND arborio-sources + arbornml.cpp + parse_morphology.cpp + with_xml.cpp + xmlwrap.cpp + ) +endif() + +find_package(LibXml2 REQUIRED) add_library(arborio ${arborio-sources}) @@ -10,7 +20,16 @@ target_include_directories(arborio-public-headers INTERFACE $<INSTALL_INTERFACE:include> ) -target_link_libraries(arborio PUBLIC arbor arborio-public-headers) +if(ARB_WITH_NEUROML) + target_link_libraries(arborio PUBLIC arbor arborio-public-headers LibXml2::LibXml2) + list(APPEND arbor_export_dependencies "LibXml2") + set(arbor_export_dependencies "${arbor_export_dependencies}" PARENT_SCOPE) + list(APPEND arbor_supported_components "neuroml") + set(arbor_supported_components "${arbor_supported_components}" PARENT_SCOPE) +else () + target_link_libraries(arborio PUBLIC arbor arborio-public-headers) +endif() + target_link_libraries(arborio PRIVATE arbor-config-defs arborio-private-deps) install(DIRECTORY include/arborio diff --git a/arbornml/arbornml.cpp b/arborio/arbornml.cpp similarity index 60% rename from arbornml/arbornml.cpp rename to arborio/arbornml.cpp index 15c5f5551d4662c438d970d5b708ccdd4e9e9012..0ee14b0814f425eed81c281da00628660ec05fc9 100644 --- a/arbornml/arbornml.cpp +++ b/arborio/arbornml.cpp @@ -3,8 +3,7 @@ #include <string> #include <vector> -#include <arbornml/arbornml.hpp> -#include <arbornml/nmlexcept.hpp> +#include <arborio/arbornml.hpp> #include "parse_morphology.hpp" #include "xmlwrap.hpp" @@ -12,7 +11,57 @@ using std::optional; using std::nullopt; -namespace arbnml { +namespace arborio { + +static std::string fmt_error(const char* prefix, const std::string& err, unsigned line) { + return prefix + (line==0? err: "line " + std::to_string(line) + ": " + err); +} + +xml_error::xml_error(const std::string& xml_error_msg, unsigned line): + neuroml_exception(fmt_error("xml error: ", xml_error_msg, line)), + xml_error_msg(xml_error_msg), + line(line) +{} + +no_document::no_document(): + neuroml_exception("no NeuroML document to parse") +{} + +parse_error::parse_error(const std::string& error_msg, unsigned line): + neuroml_exception(fmt_error("parse error: ", error_msg, line)), + error_msg(error_msg), + line(line) +{} + +bad_segment::bad_segment(unsigned long long segment_id, unsigned line): + neuroml_exception( + fmt_error( + "bad morphology segment: ", + "segment "+(segment_id+1==0? "unknown": "\""+std::to_string(segment_id)+"\""), + line)), + segment_id(segment_id), + line(line) +{} + +bad_segment_group::bad_segment_group(const std::string& group_id, unsigned line): + neuroml_exception( + fmt_error( + "bad morphology segmentGroup: ", + "segmentGroup id "+(group_id.empty()? "unknown": "\""+group_id+"\""), + line)), + group_id(group_id), + line(line) +{} + +cyclic_dependency::cyclic_dependency(const std::string& id, unsigned line): + neuroml_exception( + fmt_error( + "cyclic dependency: ", + "element id \""+id+"\"", + line)), + id(id), + line(line) +{} struct neuroml_impl { xml_doc doc; @@ -93,4 +142,4 @@ optional<morphology_data> neuroml::cell_morphology(const std::string& cell_id) c return M; } -} // namespace arbnml +} // namespace arborio diff --git a/arbornml/include/arbornml/arbornml.hpp b/arborio/include/arborio/arbornml.hpp similarity index 54% rename from arbornml/include/arbornml/arbornml.hpp rename to arborio/include/arborio/arbornml.hpp index b925aaedebdd35176a0d659bf862fdf0c006b7c6..0f3d939e5049450245754201889eeabcd12abfda 100644 --- a/arbornml/include/arbornml/arbornml.hpp +++ b/arborio/include/arborio/arbornml.hpp @@ -1,5 +1,7 @@ #pragma once +#include <cstddef> +#include <stdexcept> #include <optional> #include <memory> #include <string> @@ -9,7 +11,58 @@ #include <arbor/morph/label_dict.hpp> #include <arbor/morph/morphology.hpp> -namespace arbnml { +namespace arborio { + +// Common base-class for neuroml run-time errors. +struct neuroml_exception: std::runtime_error { + neuroml_exception(const std::string& what_arg): + std::runtime_error(what_arg) + {} +}; + +// Generic XML error (as reported by libxml2). +struct xml_error: neuroml_exception { + xml_error(const std::string& xml_error_msg, unsigned line = 0); + std::string xml_error_msg; + unsigned line; +}; + +// Can't parse NeuroML if we don't have a document. +struct no_document: neuroml_exception { + no_document(); +}; + +// Generic error parsing NeuroML data. +struct parse_error: neuroml_exception { + parse_error(const std::string& error_msg, unsigned line = 0); + std::string error_msg; + unsigned line; +}; + +// NeuroML morphology error: improper segment data, e.g. bad id specification, +// segment parent does not exist, fractionAlong is out of bounds, missing +// required <proximal> data. +struct bad_segment: neuroml_exception { + bad_segment(unsigned long long segment_id, unsigned line = 0); + unsigned long long segment_id; + unsigned line; +}; + +// NeuroML morphology error: improper segmentGroup data, e.g. malformed +// element data, missing referenced segments or groups, etc. +struct bad_segment_group: neuroml_exception { + bad_segment_group(const std::string& group_id, unsigned line = 0); + std::string group_id; + unsigned line; +}; + +// A segment or segmentGroup ultimately refers to itself via `parent` +// or `include` elements respectively. +struct cyclic_dependency: neuroml_exception { + cyclic_dependency(const std::string& id, unsigned line = 0); + std::string id; + unsigned line; +}; // Collect and parse morphology elements from XML. // No validation is performed against the NeuroML v2 schema. @@ -71,4 +124,4 @@ private: std::unique_ptr<neuroml_impl> impl_; }; -} // namespace arbnml +} // namespace arborio diff --git a/arbornml/include/arbornml/with_xml.hpp b/arborio/include/arborio/with_xml.hpp similarity index 80% rename from arbornml/include/arbornml/with_xml.hpp rename to arborio/include/arborio/with_xml.hpp index c308451f7ba5618450cdbb79f3d156216a73ce0d..d176daa00413a182869fcec94aa61d12de45e42a 100644 --- a/arbornml/include/arbornml/with_xml.hpp +++ b/arborio/include/arborio/with_xml.hpp @@ -2,11 +2,11 @@ // Wrap initialization and cleanup of libxml2 library. // -// Use of `with_xml` is only necessary if arbornml is being +// Use of `with_xml` is only necessary if arborio is being // used in a multithreaded context and the client code is // not managing libxml2 initialization and cleanup. -namespace arbnml { +namespace arborio { struct with_xml { with_xml(); @@ -21,4 +21,4 @@ struct with_xml { bool run_cleanup_; }; -} // namespace arbnml +} // namespace arborio diff --git a/arbornml/parse_morphology.cpp b/arborio/parse_morphology.cpp similarity index 99% rename from arbornml/parse_morphology.cpp rename to arborio/parse_morphology.cpp index 3f3cbf556c5903fc0825d4e577f41e18064a7f9b..d7adc67a7a1edc28aa64d0a601322e01ba37ce8b 100644 --- a/arbornml/parse_morphology.cpp +++ b/arborio/parse_morphology.cpp @@ -14,8 +14,7 @@ #include <arbor/morph/stitch.hpp> #include <arbor/util/expected.hpp> -#include <arbornml/arbornml.hpp> -#include <arbornml/nmlexcept.hpp> +#include <arborio/arbornml.hpp> #include "parse_morphology.hpp" #include "xmlwrap.hpp" @@ -25,7 +24,7 @@ using arb::region; using arb::util::expected; using arb::util::unexpected; -namespace arbnml { +namespace arborio { // Box is a container of size 0 or 1. @@ -554,4 +553,4 @@ morphology_data parse_morphology_element(xml_xpathctx ctx, xml_node morph) { return M; } -} // namespace arbnml +} // namespace arborio diff --git a/arbornml/parse_morphology.hpp b/arborio/parse_morphology.hpp similarity index 60% rename from arbornml/parse_morphology.hpp rename to arborio/parse_morphology.hpp index 305dcb6b13f694437a9686253bf09ac3235d69a6..0ced56cd683067a4ebc12fc94c9fafdd82fa6354 100644 --- a/arbornml/parse_morphology.hpp +++ b/arborio/parse_morphology.hpp @@ -1,10 +1,10 @@ #pragma once -#include <arbornml/arbornml.hpp> +#include <arborio/arbornml.hpp> #include "xmlwrap.hpp" -namespace arbnml { +namespace arborio { morphology_data parse_morphology_element(xml_xpathctx ctx, xml_node morph); -} // namespace arbnml +} // namespace arborio diff --git a/arbornml/with_xml.cpp b/arborio/with_xml.cpp similarity index 83% rename from arbornml/with_xml.cpp rename to arborio/with_xml.cpp index c7ae41d0278d61053aabb73f3a741ee8d9c2bb0d..269c174d31b6da67a4f2fd841150ad5065079f8a 100644 --- a/arbornml/with_xml.cpp +++ b/arborio/with_xml.cpp @@ -1,8 +1,8 @@ -#include <arbornml/with_xml.hpp> +#include <arborio/with_xml.hpp> #include <libxml/parser.h> -namespace arbnml { +namespace arborio { with_xml::with_xml(): run_cleanup_(true) { // Initialize before any multithreaded access by library or client code. @@ -19,4 +19,4 @@ with_xml::~with_xml() { } } -} // namespace arbnml +} // namespace arborio diff --git a/arbornml/xmlwrap.cpp b/arborio/xmlwrap.cpp similarity index 95% rename from arbornml/xmlwrap.cpp rename to arborio/xmlwrap.cpp index c46b129284a106d94cea0dba974a9f2d22b60ca1..126f2ca6f1ff740cfbc4215c7e63e2a9f1efcafb 100644 --- a/arbornml/xmlwrap.cpp +++ b/arborio/xmlwrap.cpp @@ -12,11 +12,9 @@ #include <libxml/xmlerror.h> -#include <arbornml/nmlexcept.hpp> - #include "xmlwrap.hpp" -namespace arbnml { +namespace arborio { namespace detail { @@ -95,14 +93,14 @@ void throw_on_xml_generic_error(void *, const char* msg, ...) { vsnprintf(&err[0], err.size(), msg, vb); va_end(vb); - throw ::arbnml::xml_error(err); + throw ::arborio::xml_error(err); } void throw_on_xml_structured_error(void *ctx, xmlErrorPtr errp) { if (errp->level!=1) { // ignore warnings! std::string msg(errp->message); if (!msg.empty() && msg.back()=='\n') msg.pop_back(); - throw ::arbnml::xml_error(msg, errp->line); + throw ::arborio::xml_error(msg, errp->line); } } @@ -125,4 +123,4 @@ xml_error_scope::~xml_error_scope() { xmlStructuredErrorContext = structured_context_; } -} // namespace arbnml +} // namespace arborio diff --git a/arbornml/xmlwrap.hpp b/arborio/xmlwrap.hpp similarity index 98% rename from arbornml/xmlwrap.hpp rename to arborio/xmlwrap.hpp index d914bc69f3009d358bcd948675b7f108a48373e2..0993b9ba0665d5f7002c614ef416a69cea007667 100644 --- a/arbornml/xmlwrap.hpp +++ b/arborio/xmlwrap.hpp @@ -14,9 +14,9 @@ #include <libxml/xpath.h> #include <libxml/xpathInternals.h> -#include <arbornml/nmlexcept.hpp> +#include "arborio/arbornml.hpp" -namespace arbnml { +namespace arborio { // `non_negative` represents the corresponding constraint in the schema, which // can mean any arbitrarily large non-negtative integer value. @@ -300,7 +300,7 @@ inline std::string xpath_escape(const std::string& x) { // xml_error_scope object will restore the original error handling // behaviour on destruction. // -// Errors are turned into arbnml::xml_error exceptions and thrown, +// Errors are turned into arborio::xml_error exceptions and thrown, // while warnings are ignored (libxml2 warnings are highly innocuous). struct xml_error_scope { @@ -314,4 +314,4 @@ struct xml_error_scope { void* structured_context_; }; -} // namespace arbnml +} // namespace arborio diff --git a/arbornml/CMakeLists.txt b/arbornml/CMakeLists.txt deleted file mode 100644 index a4d944980f8402f18dc731672caf817a9212eb5e..0000000000000000000000000000000000000000 --- a/arbornml/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -set(arbornml-sources - arbornml.cpp - nmlexcept.cpp - parse_morphology.cpp - with_xml.cpp - xmlwrap.cpp -) - -find_package(LibXml2 REQUIRED) - -add_library(arbornml ${arbornml-sources}) - -add_library(arbornml-public-headers INTERFACE) -target_include_directories(arbornml-public-headers INTERFACE - $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> - $<INSTALL_INTERFACE:include> -) - -target_link_libraries(arbornml PUBLIC arbor arbornml-public-headers LibXml2::LibXml2) -target_link_libraries(arbornml PRIVATE arbor-config-defs arbornml-private-deps) - -install(DIRECTORY include/arbornml - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING PATTERN "*.hpp") - -install(TARGETS arbornml-public-headers EXPORT arbor-targets) -install(TARGETS arbornml EXPORT arbor-targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - -list(APPEND arbor_export_dependencies "LibXml2") -set(arbor_export_dependencies "${arbor_export_dependencies}" PARENT_SCOPE) -list(APPEND arbor_supported_components "neuroml") -set(arbor_supported_components "${arbor_supported_components}" PARENT_SCOPE) diff --git a/arbornml/include/arbornml/nmlexcept.hpp b/arbornml/include/arbornml/nmlexcept.hpp deleted file mode 100644 index bba15a80062678fc53846b9437e2c3e1032e9b10..0000000000000000000000000000000000000000 --- a/arbornml/include/arbornml/nmlexcept.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include <cstddef> -#include <stdexcept> -#include <string> - -namespace arbnml { - -// Common base-class for arbnml run-time errors. - -struct neuroml_exception: std::runtime_error { - neuroml_exception(const std::string& what_arg): - std::runtime_error(what_arg) - {} -}; - -// Generic XML error (as reported by libxml2). - -struct xml_error: neuroml_exception { - xml_error(const std::string& xml_error_msg, unsigned line = 0); - std::string xml_error_msg; - unsigned line; -}; - -// Can't parse NeuroML if we don't have a document. - -struct no_document: neuroml_exception { - no_document(); -}; - -// Generic error parsing NeuroML data. - -struct parse_error: neuroml_exception { - parse_error(const std::string& error_msg, unsigned line = 0); - std::string error_msg; - unsigned line; -}; - -// NeuroML morphology error: improper segment data, e.g. bad id specification, -// segment parent does not exist, fractionAlong is out of bounds, missing -// required <proximal> data. - -struct bad_segment: neuroml_exception { - bad_segment(unsigned long long segment_id, unsigned line = 0); - unsigned long long segment_id; - unsigned line; -}; - -// NeuroML morphology error: improper segmentGroup data, e.g. malformed -// element data, missing referenced segments or groups, etc. - -struct bad_segment_group: neuroml_exception { - bad_segment_group(const std::string& group_id, unsigned line = 0); - std::string group_id; - unsigned line; -}; - -// A segment or segmentGroup ultimately refers to itself via `parent` -// or `include` elements respectively. - -struct cyclic_dependency: neuroml_exception { - cyclic_dependency(const std::string& id, unsigned line = 0); - std::string id; - unsigned line; -}; - -} // namespace arbnml diff --git a/arbornml/nmlexcept.cpp b/arbornml/nmlexcept.cpp deleted file mode 100644 index 7ad1c8cac289b0d5ea869e8db40173c5b27be392..0000000000000000000000000000000000000000 --- a/arbornml/nmlexcept.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include <string> - -#include <arbornml/nmlexcept.hpp> - -namespace arbnml { - -static std::string fmt_error(const char* prefix, const std::string& err, unsigned line) { - return prefix + (line==0? err: "line " + std::to_string(line) + ": " + err); -} - -xml_error::xml_error(const std::string& xml_error_msg, unsigned line): - neuroml_exception(fmt_error("xml error: ", xml_error_msg, line)), - xml_error_msg(xml_error_msg), - line(line) -{} - -no_document::no_document(): - neuroml_exception("no NeuroML document to parse") -{} - -parse_error::parse_error(const std::string& error_msg, unsigned line): - neuroml_exception(fmt_error("parse error: ", error_msg, line)), - error_msg(error_msg), - line(line) -{} - -bad_segment::bad_segment(unsigned long long segment_id, unsigned line): - neuroml_exception( - fmt_error( - "bad morphology segment: ", - "segment "+(segment_id+1==0? "unknown": "\""+std::to_string(segment_id)+"\""), - line)), - segment_id(segment_id), - line(line) -{} - -bad_segment_group::bad_segment_group(const std::string& group_id, unsigned line): - neuroml_exception( - fmt_error( - "bad morphology segmentGroup: ", - "segmentGroup id "+(group_id.empty()? "unknown": "\""+group_id+"\""), - line)), - group_id(group_id), - line(line) -{} - -cyclic_dependency::cyclic_dependency(const std::string& id, unsigned line): - neuroml_exception( - fmt_error( - "cyclic dependency: ", - "element id \""+id+"\"", - line)), - id(id), - line(line) -{} - -} // namespace arbnml diff --git a/cmake/arbor-config.cmake.in b/cmake/arbor-config.cmake.in index d551de69d3108bfc7ff2f8553f0e6ae3bcc256db..d7b056d91738ae3c8fb1f05565c2da9cf906ed1b 100644 --- a/cmake/arbor-config.cmake.in +++ b/cmake/arbor-config.cmake.in @@ -21,7 +21,7 @@ endforeach() set(_override_lang @arbor_override_import_lang@) if(_override_lang) - foreach(target arbor::arbor arbor::arborenv arbor::arbornml) + foreach(target arbor::arbor arbor::arborenv arbor::arborio) if(TARGET ${target}) set_target_properties(${target} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_@arbor_build_config@ "${_override_lang}") endif() @@ -53,5 +53,5 @@ set(ARB_CXXOPT_ARCH @ARB_CXXOPT_ARCH@) _append_property(arbor::arbor INTERFACE_LINK_LIBRARIES @arbor_add_import_libs@) _append_property(arbor::arborenv INTERFACE_LINK_LIBRARIES @arborenv_add_import_libs@) -_append_property(arbor::arbornml INTERFACE_LINK_LIBRARIES @arbornml_add_import_libs@) +_append_property(arbor::arborio INTERFACE_LINK_LIBRARIES @arborio_add_import_libs@) diff --git a/doc/cpp/neuroml.rst b/doc/cpp/neuroml.rst index d495b6616f3af04cbcc9e0cecb9ef885512772ca..290e74b2e698444640b08b742ccc5c72d6221097 100644 --- a/doc/cpp/neuroml.rst +++ b/doc/cpp/neuroml.rst @@ -8,32 +8,31 @@ Arbor offers limited support for models described in This is not built by default, but can be enabled by providing the `-DARB_NEUROML=ON` argument to CMake at configuration time (see :ref:`install-neuroml`). This will -build the ``arbornml`` libray and defines the corresponding -``arbor::arbornml`` CMake target. +build the ``arborio`` libray with neuroml support. -The ``arbornml`` library uses `libxml2 <http://xmlsoft.org/>`_ -for XML parsing. Applications using ``arbornml`` will need to -link against ``libxml2`` in addition, though this is performed -implicitly within CMake projects that add ``arbor::arbornml`` +The ``arborio`` library uses `libxml2 <http://xmlsoft.org/>`_ +for XML parsing. Applications using NeuroML through ``arborio`` +will need to link against ``libxml2`` in addition, though this +is performed implicitly within CMake projects that add ``arbor::arborio`` as a link library. -All classes and functions provided by the ``arbornml`` library -are provided in the ``arbnml`` namespace. +All classes and functions provided by the ``arborio`` library +are provided in the ``arborio`` namespace. Libxml2 interface ----------------- Libxml2 offers threadsafe XML parsing, but not by default. If -the application uses ``arbornml`` in an unthreaded context, or -has already explicitly initialized ``libxml2``, nothing more -needs to be done. Otherwise, the ``libxml2`` function ``xmlInitParser()`` -must be called explicitly. +the application uses NeuromML support from ``arborio`` in an +unthreaded context, or has already explicitly initialized ``libxml2``, +nothing more needs to be done. Otherwise, the ``libxml2`` function +``xmlInitParser()`` must be called explicitly. -``arbornml`` provides a helper guard object for this purpose, defined -in ``arbornml/with_xml.hpp``: +``arborio`` provides a helper guard object for this purpose, defined +in ``arborio/with_xml.hpp``: -.. cpp:namespace:: arbnml +.. cpp:namespace:: arborio .. cpp:class:: with_xml @@ -44,11 +43,11 @@ in ``arbornml/with_xml.hpp``: NeuroML 2 morphology support ---------------------------- -NeuroML documents are represented by the ``arbnml::neuroml`` class, +NeuroML documents are represented by the ``arborio::neuroml`` class, which in turn provides methods for the identification and translation of morphology data. ``neuroml`` objects are moveable and move-assignable, but not copyable. -An implementation limitation restrictes valid segment id values to +An implementation limitation restricts valid segment id values to those which can be represented by an ``unsigned long long`` value. .. cpp:class:: neuroml @@ -111,14 +110,14 @@ segment group. .. cpp:member:: std::unordered_map<std::string, std::vector<unsigned long long>> group_segments - A map from taking each segment group id to its corresponding collection of segments. + A map from each segment group id to its corresponding collection of segments. Exceptions ---------- -All NeuroML-specific exceptions are defined in ``arbornml/nmlexcept.hpp``, and are -derived from ``arbnml::neuroml_exception`` which in turn is derived from ``std::runtime_error``. +All NeuroML-specific exceptions are defined in ``arborio/arbornml.hpp``, and are +derived from ``arborio::neuroml_exception`` which in turn is derived from ``std::runtime_error``. With the exception of the ``no_document`` exception, all contain an unsigned member ``line`` which is intended to identify the problematic construct within the document. diff --git a/doc/install/build_install.rst b/doc/install/build_install.rst index c1810c3b699d35e8f2504a729f733ee176def754..7181512c400ba5c03c833a99852637511510947c 100644 --- a/doc/install/build_install.rst +++ b/doc/install/build_install.rst @@ -129,9 +129,10 @@ NeuroML ~~~~~~~ Arbor supports reading cell morphologies defined in NeuroML version 2 through -an additional NeuroML support library ``arbornml``. This library requires -`libxml2 <http://xmlsoft.org>`_ for the parsing of NeuroML2 XML. See :ref:`install-neuroml` for -more information. +an additional support library ``arborio``. This library requires +`libxml2 <http://xmlsoft.org>`_ for the parsing of NeuroML2 XML, if it is built +with NeuroML support enabled. +See :ref:`install-neuroml` for more information. Documentation @@ -485,27 +486,27 @@ NeuroML support --------------- Arbor has limited support for NeuroML version 2 through an additional library -``arbornml``. This library will be built if the option ``-DARB_WITH_NEUROML=ON`` -is passed to CMake at configuration time. ``arbornml`` depends upon the -the ``libxml2`` library for XML parsing. +``arborio``. This library will be built with NeuroML support if the option +``-DARB_WITH_NEUROML=ON`` is passed to CMake at configuration time. +``arborio`` depends upon the the ``libxml2`` library for XML parsing. -With NeuroML support enabled, Arbor will additionally install the static library -``libarbornml.a``. Applications using this functionality will need to link +Arbor will additionally install the static library ``libarborio.a``. +Applications using this functionality will need to link against this library in addition to the main Arbor library and ``libxml2``. For example: .. code-block:: bash - g++ -std=c++17 -pthread mycode.cpp -larbornml -larbor -lxml2 + g++ -std=c++17 -pthread mycode.cpp -larborio -larbor -lxml2 For projects using CMake, Arbor NeuroML support can be required with the -component ``neuroml``. The corresponding CMake library target is ``arbor::arbornml``. +component ``neuroml``. The corresponding CMake library target is ``arbor::arborio``. .. code-block:: cmake find_package(arbor COMPONENTS neuroml) # ... - target_link_libraries(myapp arbor::arbornml) + target_link_libraries(myapp arbor::arborio) .. _install: diff --git a/doc/python/morphology.rst b/doc/python/morphology.rst index ef9eabb5be25c60ffb973f76d29bb67f128db841..8ffa546686379541589a8527da5df8e598d37965 100644 --- a/doc/python/morphology.rst +++ b/doc/python/morphology.rst @@ -482,3 +482,86 @@ Cell morphology Compose the two isometries to form a new isometry that applies *b* and then applies *a*. Note that rotations are composed as being with respect to the *intrinsic* coordinate system, while translations are always taken to be with respect to the *extrinsic* absolute coordinate system. + + +.. py:class:: neuroml_morph_data + + A :class:`neuroml_morphology_data` object contains a representation of a morphology defined in + NeuroML. + + + .. py:attribute:: cell_id + :type: optional<str> + + The id attribute of the cell that was used to find the morphology in the NeuroML document, if any. + + .. py:attribute:: id + :type: str + + The id attribute of the morphology. + + .. py:attribute:: group_segments + :type: dict[str, list[long]] + + A map from each segment group id to its corresponding collection of segments. + + .. py:method:: segments + + Returns a label dictionary with a region entry for each segment, keyed by the segment id (as a string). + + :rtype: label_dict + + .. py:method:: named_segments + + Returns a label dictionary with a region entry for each name attribute given to one or more segments. + The region corresponds to the union of all segments sharing the same name attribute. + + :rtype: label_dict + + .. py:method:: groups + + Returns a label dictionary with a region entry for each defined segment group. + + :rtype: label_dict + +.. py:class:: neuroml + + A :class:`neuroml` object represent NeuroML documents, and provides methods for the identification and + translation of morphology data. + + An implementation limitation restricts valid segment id values to those which can be represented by an + unsigned long long value. + + .. py:method:: neuroml(filename) + + Build a NeuroML document representation from the supplied file contents. + + :param str filename: the name of the NeuroML file. + + .. py:method:: cell_ids() + + Return the id of each ``<cell>`` element defined in the NeuroML document. + + :rtype: list[str] + + .. py:method:: morphology_ids() + + Return the id of each top-level ``<morphology>`` element defined in the NeuroML document. + + :rtype: list[str] + + .. py:method:: morphology(morph_id) + + Returns a representation of the top-level morphology with the supplied morph_id if it could be found. + Parse errors or an inconsistent representation will raise an exception. + + :param str morph_id: ID of the top-level morphology. + :rtype: optional(neuroml_morph_data) + + .. py:method:: cell_morphology(cell_id) + + Returns a representation of the morphology associated with the cell with the supplied cell_id if it + could be found. Parse errors or an inconsistent representation will raise an exception. + + :param str morph_id: ID of the cell. + :rtype: optional(neuroml_morph_data) \ No newline at end of file diff --git a/python/cells.cpp b/python/cells.cpp index bc9d15468b82964bf630ef9d5a9cefb3773e44be..2f610f569ef3e798a922f387b0cd6d56a888d18d 100644 --- a/python/cells.cpp +++ b/python/cells.cpp @@ -28,105 +28,13 @@ #include "arbor/cv_policy.hpp" #include "conversion.hpp" #include "error.hpp" +#include "proxy.hpp" #include "pybind11/cast.h" #include "pybind11/pytypes.h" #include "schedule.hpp" #include "strprintf.hpp" namespace pyarb { -// -// proxies -// - -struct label_dict_proxy { - using str_map = std::unordered_map<std::string, std::string>; - arb::label_dict dict; - str_map cache; - std::vector<std::string> locsets; - std::vector<std::string> regions; - - label_dict_proxy() = default; - - label_dict_proxy(const str_map& in) { - for (auto& i: in) { - set(i.first.c_str(), i.second.c_str()); - } - } - - std::size_t size() const { - return locsets.size() + regions.size(); - } - - void set(const char* name, const char* desc) { - using namespace std::string_literals; - // The following code takes an input name and a region or locset - // description, e.g.: - // name='reg', desc='(tag 4)' - // name='loc', desc='(terminal)' - // name='foo', desc='(join (tag 2) (tag 3))' - // Then it parses the description, and tests whether the description - // is a region or locset, and updates the label dictionary appropriately. - // Errors occur when: - // * a region is described with a name that matches an existing locset - // (and vice versa.) - // * the description is not well formed, e.g. it contains a syntax error. - // * the description is well-formed, but describes neither a region or locset. - try{ - // Evaluate the s-expression to build a region/locset. - auto result = arb::parse_label_expression(desc); - if (!result) { // an error parsing / evaluating description. - throw result.error(); - } - else if (result->type()==typeid(arb::region)) { // describes a region. - dict.set(name, std::move(std::any_cast<arb::region&>(*result))); - auto it = std::lower_bound(regions.begin(), regions.end(), name); - if (it==regions.end() || *it!=name) regions.insert(it, name); - } - else if (result->type()==typeid(arb::locset)) { // describes a locset. - dict.set(name, std::move(std::any_cast<arb::locset&>(*result))); - auto it = std::lower_bound(locsets.begin(), locsets.end(), name); - if (it==locsets.end() || *it!=name) locsets.insert(it, name); - } - else { - // Successfully parsed an expression that is neither region nor locset. - throw util::pprintf("The defninition of '{} = {}' does not define a valid region or locset.", name, desc); - } - // The entry was added succesfully: store it in the cache. - cache[name] = desc; - } - catch (std::string msg) { - const char* base = "\nError adding the label '{}' = '{}'\n{}\n"; - - throw std::runtime_error(util::pprintf(base, name, desc, msg)); - } - // Exceptions are thrown in parse or eval if an unexpected error occured. - catch (std::exception& e) { - const char* msg = - "\n----- internal error -------------------------------------------" - "\nError parsing the label: '{}' = '{}'" - "\n" - "\n{}" - "\n" - "\nPlease file a bug report with this full error message at:" - "\n github.com/arbor-sim/arbor/issues" - "\n----------------------------------------------------------------"; - throw arb::arbor_internal_error(util::pprintf(msg, name, desc, e.what())); - } - } - - std::string to_string() const { - std::string s; - s += "(label_dict"; - for (auto& x: dict.regions()) { - s += util::pprintf(" (region \"{}\" {})", x.first, x.second); - } - for (auto& x: dict.locsets()) { - s += util::pprintf(" (locset \"{}\" {})", x.first, x.second); - } - s += ")"; - return s; - } -}; // This isn't pretty. Partly because the information in the global parameters // is all over the place. diff --git a/python/morphology.cpp b/python/morphology.cpp index 06100d9f41884b1187d07dfa162eac2330d381fb..aa0425b4ba18d46170c3c6118cee63867968b481 100644 --- a/python/morphology.cpp +++ b/python/morphology.cpp @@ -10,10 +10,16 @@ #include <arbor/morph/place_pwlin.hpp> #include <arbor/morph/primitives.hpp> #include <arbor/morph/segment_tree.hpp> +#include <arbor/version.hpp> #include <arborio/swcio.hpp> +#ifdef ARB_NEUROML_ENABLED +#include <arborio/arbornml.hpp> +#endif + #include "error.hpp" +#include "proxy.hpp" #include "strprintf.hpp" namespace py = pybind11; @@ -363,6 +369,91 @@ void register_morphology(py::module& m) { [](const arb::morphology& m) { return util::pprintf("<arbor.morphology:\n{}>", m); }); + +#ifdef ARB_NEUROML_ENABLED + // arborio::morphology_data + py::class_<arborio::morphology_data> nml_morph_data(m, "neuroml_morph_data"); + nml_morph_data + .def_readonly("cell_id", + &arborio::morphology_data::cell_id, + "Cell id, or empty if morphology was taken from a top-level <morphology> element.") + .def_readonly("id", + &arborio::morphology_data::id, + "Morphology id.") + .def_readonly("morphology", + &arborio::morphology_data::morphology, + "Morphology constructed from a signle NeuroML <morphology> element.") + .def("segments", + [](const arborio::morphology_data& md) {return label_dict_proxy(md.segments);}, + "Label dictionary containing one region expression for each segment id.") + .def("named_segments", + [](const arborio::morphology_data& md) {return label_dict_proxy(md.named_segments);}, + "Label dictionary containing one region expression for each name applied to one or more segments.") + .def("groups", + [](const arborio::morphology_data& md) {return label_dict_proxy(md.groups);}, + "Label dictionary containing one region expression for each segmentGroup id.") + .def_readonly("group_segments", + &arborio::morphology_data::group_segments, + "Map from segmentGroup ids to their corresponding segment ids."); + + // arborio::neuroml + py::class_<arborio::neuroml> neuroml(m, "neuroml"); + neuroml + // constructors + .def(py::init( + [](std::string fname){ + std::ifstream fid{fname}; + if (!fid.good()) { + throw pyarb_error(util::pprintf("can't open file '{}'", fname)); + } + try { + std::string string_data((std::istreambuf_iterator<char>(fid)), + std::istreambuf_iterator<char>()); + return arborio::neuroml(string_data); + } + catch (arborio::neuroml_exception& e) { + // Try to produce helpful error messages for SWC parsing errors. + throw pyarb_error( + util::pprintf("NeuroML error processing file {}: ", fname, e.what())); + } + })) + .def("cell_ids", + [](const arborio::neuroml& nml) { + try { + return nml.cell_ids(); + } + catch (arborio::neuroml_exception& e) { + throw util::pprintf("NeuroML error: {}", e.what()); + } + },"Query top-level cells.") + .def("morphology_ids", + [](const arborio::neuroml& nml) { + try { + return nml.morphology_ids(); + } + catch (arborio::neuroml_exception& e) { + throw util::pprintf("NeuroML error: {}", e.what()); + } + },"Query top-level standalone morphologies.") + .def("morphology", + [](const arborio::neuroml& nml, const std::string& morph_id) { + try { + return nml.morphology(morph_id); + } + catch (arborio::neuroml_exception& e) { + throw util::pprintf("NeuroML error: {}", e.what()); + } + },"morph_id"_a,"Retrieve top-level nml_morph_data associated with morph_id.") + .def("cell_morphology", + [](const arborio::neuroml& nml, const std::string& cell_id) { + try { + return nml.cell_morphology(cell_id); + } + catch (arborio::neuroml_exception& e) { + throw util::pprintf("NeuroML error: {}", e.what()); + } + },"morph_id"_a,"Retrieve nml_morph_data associated with cell_id."); +#endif } } // namespace pyarb diff --git a/python/proxy.hpp b/python/proxy.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d47ab761b745d7947bd80ccd65d1f8d00ff5582d --- /dev/null +++ b/python/proxy.hpp @@ -0,0 +1,102 @@ +#pragma once + +#include <any> + +#include <arbor/morph/label_dict.hpp> +#include <arbor/morph/label_parse.hpp> + +#include "strprintf.hpp" + +namespace pyarb { +struct label_dict_proxy { + using str_map = std::unordered_map<std::string, std::string>; + arb::label_dict dict; + str_map cache; + std::vector<std::string> locsets; + std::vector<std::string> regions; + + label_dict_proxy() = default; + + label_dict_proxy(const str_map& in) { + for (auto& i: in) { + set(i.first.c_str(), i.second.c_str()); + } + } + + label_dict_proxy(const arb::label_dict& label_dict): dict(label_dict) {} + + std::size_t size() const { + return locsets.size() + regions.size(); + } + + void set(const char* name, const char* desc) { + using namespace std::string_literals; + // The following code takes an input name and a region or locset + // description, e.g.: + // name='reg', desc='(tag 4)' + // name='loc', desc='(terminal)' + // name='foo', desc='(join (tag 2) (tag 3))' + // Then it parses the description, and tests whether the description + // is a region or locset, and updates the label dictionary appropriately. + // Errors occur when: + // * a region is described with a name that matches an existing locset + // (and vice versa.) + // * the description is not well formed, e.g. it contains a syntax error. + // * the description is well-formed, but describes neither a region or locset. + try{ + // Evaluate the s-expression to build a region/locset. + auto result = arb::parse_label_expression(desc); + if (!result) { // an error parsing / evaluating description. + throw result.error(); + } + else if (result->type()==typeid(arb::region)) { // describes a region. + dict.set(name, std::move(std::any_cast<arb::region&>(*result))); + auto it = std::lower_bound(regions.begin(), regions.end(), name); + if (it==regions.end() || *it!=name) regions.insert(it, name); + } + else if (result->type()==typeid(arb::locset)) { // describes a locset. + dict.set(name, std::move(std::any_cast<arb::locset&>(*result))); + auto it = std::lower_bound(locsets.begin(), locsets.end(), name); + if (it==locsets.end() || *it!=name) locsets.insert(it, name); + } + else { + // Successfully parsed an expression that is neither region nor locset. + throw util::pprintf("The defninition of '{} = {}' does not define a valid region or locset.", name, desc); + } + // The entry was added succesfully: store it in the cache. + cache[name] = desc; + } + catch (std::string msg) { + const char* base = "\nError adding the label '{}' = '{}'\n{}\n"; + + throw std::runtime_error(util::pprintf(base, name, desc, msg)); + } + // Exceptions are thrown in parse or eval if an unexpected error occured. + catch (std::exception& e) { + const char* msg = + "\n----- internal error -------------------------------------------" + "\nError parsing the label: '{}' = '{}'" + "\n" + "\n{}" + "\n" + "\nPlease file a bug report with this full error message at:" + "\n github.com/arbor-sim/arbor/issues" + "\n----------------------------------------------------------------"; + throw arb::arbor_internal_error(util::pprintf(msg, name, desc, e.what())); + } + } + + std::string to_string() const { + std::string s; + s += "(label_dict"; + for (auto& x: dict.regions()) { + s += util::pprintf(" (region \"{}\" {})", x.first, x.second); + } + for (auto& x: dict.locsets()) { + s += util::pprintf(" (locset \"{}\" {})", x.first, x.second); + } + s += ")"; + return s; + } +}; +} diff --git a/setup.py b/setup.py index 8d62906ade5d3d80080539569da8deca9a239eb3..9bb49a0e13633d695252ee8d5dcea61c75b68a78 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,7 @@ class CL_opt: 'gpu': 'none', 'vec': False, 'arch': 'native', + 'neuroml': False, 'bundled': True} def settings(self): @@ -54,6 +55,7 @@ class install_command(install): 'none, cuda, cuda-clang, hip'), ('vec', None, 'enable vectorization'), ('arch=', None, 'cpu architecture, e.g. haswell, skylake, armv8.2-a+sve, znver2 (default native).'), + ('neuroml', None, 'enable parsing neuroml morphologies in Arbor (requires libxml)') ('sysdeps', None, 'don\'t use bundled 3rd party C++ dependencies (pybind11 and json). This flag forces use of dependencies installed on the system.') ] @@ -63,6 +65,7 @@ class install_command(install): self.gpu = None self.arch = None self.vec = None + self.neuroml = None self.sysdeps = None def finalize_options(self): @@ -79,6 +82,8 @@ class install_command(install): opt['vec'] = self.vec is not None # arch : target CPU micro-architecture (string). opt['arch'] = "native" if self.arch is None else self.arch + # neuroml : compile with neuroml support for morphologies. + opt['neuroml'] = self.neuroml is not None # bundled : use bundled/git-submoduled 3rd party libraries. # By default use bundled libs. opt['bundled'] = self.sysdeps is None @@ -112,6 +117,7 @@ class cmake_build(build_ext): '-DARB_VECTORIZE={}'.format('on' if opt['vec'] else 'off'), '-DARB_ARCH={}'.format(opt['arch']), '-DARB_GPU={}'.format(opt['gpu']), + '-DARB_WITH_NEUROML={}'.format( 'on' if opt['neuroml'] else 'off'), '-DARB_USE_BUNDLED_LIBS={}'.format('on' if opt['bundled'] else 'off'), '-DCMAKE_BUILD_TYPE=Release' # we compile with debug symbols in release mode. ] diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index b2474313344aa152b684188db00b5829878cf481..81de2e85a8c13e78144ee8f68feac185ce965f62 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -237,7 +237,3 @@ target_compile_definitions(unit PRIVATE "-DDATADIR=\"${CMAKE_CURRENT_SOURCE_DIR} target_compile_definitions(unit PRIVATE "-DLIBDIR=\"${PROJECT_BINARY_DIR}/lib\"") target_include_directories(unit PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") target_link_libraries(unit PRIVATE gtest arbor arborenv arborio arbor-private-headers arbor-sup) - -if(ARB_WITH_NEUROML) - target_link_libraries(unit PRIVATE arbornml) -endif() diff --git a/test/unit/test_nml_morphology.cpp b/test/unit/test_nml_morphology.cpp index 3c685a0801d8f19ddeaf73f614e15621efeba081..6860aa80543c9dbf8087f9e3491861969a260489 100644 --- a/test/unit/test_nml_morphology.cpp +++ b/test/unit/test_nml_morphology.cpp @@ -6,9 +6,8 @@ #include <arbor/morph/place_pwlin.hpp> #include <arbor/morph/primitives.hpp> -#include <arbornml/arbornml.hpp> -#include <arbornml/nmlexcept.hpp> -#include <arbornml/with_xml.hpp> +#include <arborio/arbornml.hpp> +#include <arborio/with_xml.hpp> #include "../test/gtest.h" #include "morph_pred.hpp" @@ -18,10 +17,10 @@ using testing::region_eq; TEST(neuroml, with_xml) { // This (hopefully) will not blow up. { - arbnml::with_xml scope; + arborio::with_xml scope; } { - arbnml::with_xml scope; + arborio::with_xml scope; } } @@ -30,7 +29,7 @@ TEST(neuroml, with_xml) { TEST(neuroml, morph_badxml) { std::string illformed = "<wha?"; - EXPECT_THROW(arbnml::neuroml{illformed}, arbnml::xml_error); + EXPECT_THROW(arborio::neuroml{illformed}, arborio::xml_error); } TEST(neuroml, morph_none) { @@ -38,13 +37,13 @@ TEST(neuroml, morph_none) { { std::string empty1 = R"~(<?xml version="1.0" encoding="UTF-8"?><foo/>)~"; - arbnml::neuroml N1(empty1); + arborio::neuroml N1(empty1); EXPECT_TRUE(N1.cell_ids().empty()); EXPECT_TRUE(N1.morphology_ids().empty()); std::string empty2 = "<foo/>"; - arbnml::neuroml N2(empty2); + arborio::neuroml N2(empty2); EXPECT_TRUE(N2.cell_ids().empty()); EXPECT_TRUE(N2.morphology_ids().empty()); } @@ -56,7 +55,7 @@ R"~(<?xml version="1.0" encoding="UTF-8"?> <neuroml xmlns="http://www.neuroml.org/schema/neuroml2"> </neuroml>)~"; - arbnml::neuroml N3(empty3); + arborio::neuroml N3(empty3); EXPECT_TRUE(N3.cell_ids().empty()); EXPECT_TRUE(N3.morphology_ids().empty()); } @@ -80,7 +79,7 @@ R"~( using svector = std::vector<std::string>; - arbnml::neuroml N(doc); + arborio::neuroml N(doc); svector m_ids = N.morphology_ids(); // only top-level! std::sort(m_ids.begin(), m_ids.end()); @@ -90,7 +89,7 @@ R"~( std::sort(c_ids.begin(), c_ids.end()); EXPECT_EQ((svector{"c3", "c4"}), c_ids); - arbnml::morphology_data mdata; + arborio::morphology_data mdata; mdata = N.cell_morphology("c4").value(); EXPECT_EQ("c4", mdata.cell_id); @@ -174,10 +173,10 @@ R"~( </neuroml> )~"; - arbnml::neuroml N(doc); + arborio::neuroml N(doc); { - arbnml::morphology_data m1 = N.morphology("m1").value(); + arborio::morphology_data m1 = N.morphology("m1").value(); label_dict labels; labels.import(m1.segments, "seg:"); mprovider P(m1.morphology, labels); @@ -190,7 +189,7 @@ R"~( } { - arbnml::morphology_data m2 = N.morphology("m2").value(); + arborio::morphology_data m2 = N.morphology("m2").value(); label_dict labels; labels.import(m2.segments, "seg:"); mprovider P(m2.morphology, labels); @@ -218,7 +217,7 @@ R"~( } { - arbnml::morphology_data m3 = N.morphology("m3").value(); + arborio::morphology_data m3 = N.morphology("m3").value(); label_dict labels; labels.import(m3.segments, "seg:"); mprovider P(m3.morphology, labels); @@ -252,7 +251,7 @@ R"~( } { for (const char* m_name: {"m4", "m5"}) { - arbnml::morphology_data m4_or_5 = N.morphology(m_name).value(); + arborio::morphology_data m4_or_5 = N.morphology(m_name).value(); label_dict labels; labels.import(m4_or_5.segments, "seg:"); mprovider P(m4_or_5.morphology, labels); @@ -354,14 +353,14 @@ R"~( </neuroml> )~"; - arbnml::neuroml N(doc); + arborio::neuroml N(doc); - EXPECT_THROW(N.morphology("no-proximal").value(), arbnml::bad_segment); - EXPECT_THROW(N.morphology("no-such-parent").value(), arbnml::bad_segment); - EXPECT_THROW(N.morphology("cyclic-dependency").value(), arbnml::cyclic_dependency); - EXPECT_THROW(N.morphology("duplicate-id").value(), arbnml::bad_segment); - EXPECT_THROW(N.morphology("bad-segment-id").value(), arbnml::bad_segment); - EXPECT_THROW(N.morphology("another-bad-segment-id").value(), arbnml::bad_segment); + EXPECT_THROW(N.morphology("no-proximal").value(), arborio::bad_segment); + EXPECT_THROW(N.morphology("no-such-parent").value(), arborio::bad_segment); + EXPECT_THROW(N.morphology("cyclic-dependency").value(), arborio::cyclic_dependency); + EXPECT_THROW(N.morphology("duplicate-id").value(), arborio::bad_segment); + EXPECT_THROW(N.morphology("bad-segment-id").value(), arborio::bad_segment); + EXPECT_THROW(N.morphology("another-bad-segment-id").value(), arborio::bad_segment); } TEST(neuroml, simple_groups) { @@ -441,11 +440,11 @@ R"~( </neuroml> )~"; - arbnml::neuroml N(doc); + arborio::neuroml N(doc); using reg::named; { - arbnml::morphology_data m1 = N.morphology("m1").value(); + arborio::morphology_data m1 = N.morphology("m1").value(); label_dict labels; labels.import(m1.segments); labels.import(m1.groups); @@ -456,7 +455,7 @@ R"~( EXPECT_TRUE(region_eq(P, named("group-c"), join(named("2"), named("1")))); } { - arbnml::morphology_data m2 = N.morphology("m2").value(); + arborio::morphology_data m2 = N.morphology("m2").value(); label_dict labels; labels.import(m2.segments); labels.import(m2.groups); @@ -508,11 +507,11 @@ R"~( </neuroml> )~"; - arbnml::neuroml N(doc); + arborio::neuroml N(doc); - EXPECT_THROW(N.morphology("no-such-segment").value(), arbnml::bad_segment_group); - EXPECT_THROW(N.morphology("no-such-group").value(), arbnml::bad_segment_group); - EXPECT_THROW(N.morphology("cyclic-dependency").value(), arbnml::cyclic_dependency); + EXPECT_THROW(N.morphology("no-such-segment").value(), arborio::bad_segment_group); + EXPECT_THROW(N.morphology("no-such-group").value(), arborio::bad_segment_group); + EXPECT_THROW(N.morphology("cyclic-dependency").value(), arborio::cyclic_dependency); } @@ -612,9 +611,9 @@ R"~( </neuroml> )~"; - arbnml::neuroml N(doc); + arborio::neuroml N(doc); - arbnml::morphology_data m1 = N.morphology("m1").value(); + arborio::morphology_data m1 = N.morphology("m1").value(); label_dict labels; labels.import(m1.segments); labels.import(m1.groups);