Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial attempt at implementing VERBATIM. #1364

Draft
wants to merge 21 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions docs/contents/longitudinal_diffusion.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Longitudinal Diffusion
======================

The idea behind ``LONGITUDINAL_DIFFUSION`` is to allow a ``STATE`` variable to
diffuse along a section, i.e. from one segment into a neighbouring segment.

This problem is solved by registering callbacks. In particular, NEURON needs to
be informed of the volume and diffusion rate. Additionally, the implicit
time-stepping requires information about certain derivatives.

Implementation in NMODL
-----------------------

The following ``KINETIC`` block

.. code-block::

KINETIC state {
COMPARTMENT vol {X}
LONGITUDINAL_DIFFUSION mu {X}

~ X << (ica)
}

Will undergo two transformations. The first is to create a system of ODEs that
can be solved. This consumed the AST node. However, to print the code for
longitudinal diffusion we require information from the ``COMPARTMENT`` and
``LONGITUDINAL_DIFFUSION`` statements. This is why there's a second
transformation, that runs before the other transformation, to extract the
required information and store it a AST node called
``LONGITUDINAL_DIFFUSION_BLOCK``. This block can then be converted into an
"info" object, which is then used to print the callbacks.

1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ About NMODL
contents/pointers
contents/cable_equations
contents/globals
contents/longitudinal_diffusion

.. toctree::
:maxdepth: 3
Expand Down
47 changes: 23 additions & 24 deletions src/codegen/codegen_coreneuron_cpp_visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,21 @@ std::string CodegenCoreneuronCppVisitor::process_verbatim_token(const std::strin
return get_variable_name(token, use_instance);
}

void CodegenCoreneuronCppVisitor::visit_verbatim(const Verbatim& node) {
const auto& text = node.get_statement()->eval();
printer->add_line("// VERBATIM");
const auto& result = process_verbatim_text(text);

const auto& statements = stringutils::split_string(result, '\n');
for (const auto& statement: statements) {
const auto& trimed_stmt = stringutils::trim_newline(statement);
if (trimed_stmt.find_first_not_of(' ') != std::string::npos) {
printer->add_line(trimed_stmt);
}
}
printer->add_line("// ENDVERBATIM");
}


/**
* \details This can be override in the backend. For example, parameters can be constant
Expand Down Expand Up @@ -343,28 +358,6 @@ void CodegenCoreneuronCppVisitor::print_abort_routine() const {
/****************************************************************************************/


void CodegenCoreneuronCppVisitor::print_top_verbatim_blocks() {
if (info.top_verbatim_blocks.empty()) {
return;
}
print_namespace_stop();

printer->add_newline(2);
print_using_namespace();

printing_top_verbatim_blocks = true;

for (const auto& block: info.top_verbatim_blocks) {
printer->add_newline(2);
block->accept(*this);
}

printing_top_verbatim_blocks = false;

print_namespace_start();
}


void CodegenCoreneuronCppVisitor::print_function_prototypes() {
if (info.functions.empty() && info.procedures.empty()) {
return;
Expand Down Expand Up @@ -869,7 +862,13 @@ std::string CodegenCoreneuronCppVisitor::get_variable_name(const std::string& na
auto i =
std::find_if(codegen_int_variables.begin(), codegen_int_variables.end(), index_comparator);
if (i != codegen_int_variables.end()) {
return int_variable_name(*i, varname, use_instance);
auto full_name = int_variable_name(*i, varname, use_instance);
auto pos = position_of_int_var(varname);

if (info.semantics[pos].name == naming::RANDOM_SEMANTIC) {
return "(nrnran123_State*) " + full_name;
}
return full_name;
}

// global variable
Expand Down Expand Up @@ -1677,7 +1676,7 @@ void CodegenCoreneuronCppVisitor::print_instance_variable_setup() {
printer->push_block("for (int id = 0; id < nodecount; id++)");
for (const auto& var: info.random_variables) {
const auto& name = get_variable_name(var->get_name());
printer->fmt_line("nrnran123_deletestream((nrnran123_State*){});", name);
printer->fmt_line("nrnran123_deletestream({});", name);
}
printer->pop_block();
}
Expand Down
10 changes: 2 additions & 8 deletions src/codegen/codegen_coreneuron_cpp_visitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ class CodegenCoreneuronCppVisitor: public CodegenCppVisitor {
*/
std::string process_verbatim_token(const std::string& token);


/**
* Check if variable is qualified as constant
* \param name The name of variable
Expand Down Expand Up @@ -324,12 +323,6 @@ class CodegenCoreneuronCppVisitor: public CodegenCppVisitor {
/****************************************************************************************/


/**
* Print top level (global scope) verbatim blocks
*/
void print_top_verbatim_blocks();


/**
* Print function and procedures prototype declaration
*/
Expand Down Expand Up @@ -424,7 +417,7 @@ class CodegenCoreneuronCppVisitor: public CodegenCppVisitor {
* \param text The verbatim code to be processed
* \return The code with all variables renamed as needed
*/
std::string process_verbatim_text(std::string const& text) override;
std::string process_verbatim_text(std::string const& text);


/**
Expand Down Expand Up @@ -963,6 +956,7 @@ class CodegenCoreneuronCppVisitor: public CodegenCppVisitor {

void visit_derivimplicit_callback(const ast::DerivimplicitCallback& node) override;
void visit_for_netcon(const ast::ForNetcon& node) override;
void visit_verbatim(const ast::Verbatim& node) override;
void visit_watch_statement(const ast::WatchStatement& node) override;

ParamVector functor_params() override;
Expand Down
45 changes: 23 additions & 22 deletions src/codegen/codegen_cpp_visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "ast/all.hpp"
#include "codegen/codegen_helper_visitor.hpp"
#include "codegen/codegen_utils.hpp"
#include "utils/string_utils.hpp"
#include "visitors/defuse_analyze_visitor.hpp"
#include "visitors/rename_visitor.hpp"
#include "visitors/symtab_visitor.hpp"
Expand Down Expand Up @@ -519,12 +520,6 @@ void CodegenCppVisitor::print_function_call(const FunctionCall& node) {
}
}

// first argument to random functions need to be type casted
// from void* to nrnran123_State*.
if (is_random_function && !arguments.empty()) {
printer->add_text("(nrnran123_State*)");
}

print_vector_elements(arguments, ", ");
printer->add_text(')');
}
Expand Down Expand Up @@ -639,6 +634,28 @@ void CodegenCppVisitor::print_namespace_stop() {
}


void CodegenCppVisitor::print_top_verbatim_blocks() {
if (info.top_verbatim_blocks.empty()) {
return;
}
print_namespace_stop();

printer->add_newline(2);
print_using_namespace();

printing_top_verbatim_blocks = true;

for (const auto& block: info.top_verbatim_blocks) {
printer->add_newline(2);
block->accept(*this);
}

printing_top_verbatim_blocks = false;

print_namespace_start();
}


/****************************************************************************************/
/* Printing routines for code generation */
/****************************************************************************************/
Expand Down Expand Up @@ -1048,22 +1065,6 @@ void CodegenCppVisitor::visit_function_call(const FunctionCall& node) {
}


void CodegenCppVisitor::visit_verbatim(const Verbatim& node) {
const auto& text = node.get_statement()->eval();
printer->add_line("// VERBATIM");
const auto& result = process_verbatim_text(text);

const auto& statements = stringutils::split_string(result, '\n');
for (const auto& statement: statements) {
const auto& trimed_stmt = stringutils::trim_newline(statement);
if (trimed_stmt.find_first_not_of(' ') != std::string::npos) {
printer->add_line(trimed_stmt);
}
}
printer->add_line("// ENDVERBATIM");
}


void CodegenCppVisitor::visit_update_dt(const ast::UpdateDt& node) {
// dt change statement should be pulled outside already
}
Expand Down
18 changes: 9 additions & 9 deletions src/codegen/codegen_cpp_visitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ struct ShadowUseStatement {
std::string rhs;
};

inline std::string get_name(ast::Ast const* sym) {
return sym->get_node_name();
}

inline std::string get_name(const std::shared_ptr<symtab::Symbol>& sym) {
return sym->get_name();
}
Expand Down Expand Up @@ -1031,14 +1035,6 @@ class CodegenCppVisitor: public visitor::ConstAstVisitor {
*/
virtual std::string nrn_thread_internal_arguments() = 0;

/**
* Process a verbatim block for possible variable renaming
* \param text The verbatim code to be processed
* \return The code with all variables renamed as needed
*/
virtual std::string process_verbatim_text(std::string const& text) = 0;


/**
* Arguments for register_mech or point_register_mech function
*/
Expand Down Expand Up @@ -1453,6 +1449,11 @@ class CodegenCppVisitor: public visitor::ConstAstVisitor {
*/
void print_nmodl_constants();

/**
* Print top level (global scope) verbatim blocks
*/
void print_top_verbatim_blocks();


/****************************************************************************************/
/* Overloaded visitor routines */
Expand All @@ -1478,7 +1479,6 @@ class CodegenCppVisitor: public visitor::ConstAstVisitor {
void visit_unary_operator(const ast::UnaryOperator& node) override;
void visit_unit(const ast::Unit& node) override;
void visit_var_name(const ast::VarName& node) override;
void visit_verbatim(const ast::Verbatim& node) override;
void visit_while_statement(const ast::WhileStatement& node) override;
void visit_update_dt(const ast::UpdateDt& node) override;
void visit_protect_statement(const ast::ProtectStatement& node) override;
Expand Down
59 changes: 59 additions & 0 deletions src/codegen/codegen_helper_visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@

#include <algorithm>
#include <cmath>
#include <memory>

#include "ast/all.hpp"
#include "ast/constant_var.hpp"
#include "codegen/codegen_naming.hpp"
#include "parser/c11_driver.hpp"
#include "visitors/visitor_utils.hpp"
Expand Down Expand Up @@ -853,5 +855,62 @@ void CodegenHelperVisitor::visit_after_block(const ast::AfterBlock& node) {
info.before_after_blocks.push_back(&node);
}

std::shared_ptr<ast::Compartment> find_compartment(const ast::LongitudinalDiffusionBlock& node,
const std::string& var_name) {
const auto& compartment_block = node.get_compartment_statements();
for (const auto& stmt: compartment_block->get_statements()) {
auto comp = std::dynamic_pointer_cast<ast::Compartment>(stmt);

auto species = comp->get_species();
auto it = std::find_if(species.begin(), species.end(), [&var_name](auto var) {
return var->get_node_name() == var_name;
});

if (it != species.end()) {
return comp;
}
}

return nullptr;
}

void CodegenHelperVisitor::visit_longitudinal_diffusion_block(
const ast::LongitudinalDiffusionBlock& node) {
auto longitudinal_diffusion_block = node.get_longitudinal_diffusion_statements();
for (auto stmt: longitudinal_diffusion_block->get_statements()) {
auto diffusion = std::dynamic_pointer_cast<ast::LonDiffuse>(stmt);
auto rate_index_name = diffusion->get_index_name();
auto rate_expr = diffusion->get_rate();
auto species = diffusion->get_species();

auto process_compartment = [](const std::shared_ptr<ast::Compartment>& compartment)
-> std::pair<std::shared_ptr<ast::Name>, std::shared_ptr<ast::Expression>> {
std::shared_ptr<ast::Expression> volume_expr;
std::shared_ptr<ast::Name> volume_index_name;
if (!compartment) {
volume_index_name = nullptr;
volume_expr = std::make_shared<ast::Double>("1.0");
} else {
volume_index_name = compartment->get_index_name();
volume_expr = std::shared_ptr<ast::Expression>(compartment->get_volume()->clone());
}
return {std::move(volume_index_name), std::move(volume_expr)};
};

for (auto var: species) {
std::string state_name = var->get_value()->get_value();
auto compartment = find_compartment(node, state_name);
auto [volume_index_name, volume_expr] = process_compartment(compartment);

info.longitudinal_diffusion_info.insert(
{state_name,
LongitudinalDiffusionInfo(volume_index_name,
std::shared_ptr<ast::Expression>(volume_expr),
rate_index_name,
std::shared_ptr<ast::Expression>(rate_expr->clone()))});
}
}
}

} // namespace codegen
} // namespace nmodl
1 change: 1 addition & 0 deletions src/codegen/codegen_helper_visitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ class CodegenHelperVisitor: public visitor::ConstAstVisitor {
void visit_verbatim(const ast::Verbatim& node) override;
void visit_before_block(const ast::BeforeBlock& node) override;
void visit_after_block(const ast::AfterBlock& node) override;
void visit_longitudinal_diffusion_block(const ast::LongitudinalDiffusionBlock& node) override;
};

/** @} */ // end of codegen_details
Expand Down
Loading