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

Document how to add new blocks #66

Open
menon-karthik opened this issue Nov 6, 2023 · 6 comments
Open

Document how to add new blocks #66

menon-karthik opened this issue Nov 6, 2023 · 6 comments
Assignees
Labels
documentation Improvements or additions to documentation enhancement New feature or request

Comments

@menon-karthik
Copy link
Member

It would be nice to have a guide on how to add new blocks to the solver for new developers. I am working with @kharold23 and @aabrown100-git to add a new valve block. I will try and document the steps along the way and later add that to the documentation.

@menon-karthik menon-karthik self-assigned this Nov 6, 2023
@ktbolt
Copy link
Contributor

ktbolt commented Nov 7, 2023

@menon-karthik I've been going through the code and it is was indeed not clear how to add a new element (block).

Understanding how to add a new element would be made easier if the architecture and the concepts and nomenclature used in the implementation were documented. I will open an Issue for that.

@menon-karthik menon-karthik added the enhancement New feature or request label Nov 8, 2023
@mrp089 mrp089 mentioned this issue Nov 20, 2023
1 task
@menon-karthik
Copy link
Member Author

menon-karthik commented Nov 29, 2023

Based on #79, I think these are the broad steps in adding a new block:

1. Add the new block to the following lists/dictionaries:

  • BlockType in src/model/BlockType.h
  • block_factory_map in src/model/Model.cpp. Here the dictionary key should match the string that specifies the type of block read from the JSON config file.
  • If required, BlockClass if it's a new kind of block that might require special handling (most blocks don't).

2. Create a new class inherited from Block for the new block. Specific things to pay attention to:

  • Define a constructor of the form:
    GenericBlock(int id, Model *model) : Block(id, model, BlockType::block_type, BlockClass::block_class, {{"Param1", InputParameter()}, {"Param2", InputParameter()}, ..., {"ParamN", InputParameter()}}) {}
  • In the constructor, GenericBlock is the name of the new class
  • block_type and block_class are the same as what was added in Step 1 above.
  • The input parameters of the block are Param1, ... , ParamN. The properties of each parameter is defined by InputParameter, which specifies whether it is optional, an array, a scalar, and its default value. See InputParameter in Parameter.h for details.
  • The names Param1, ... , ParamN should be the same as the strings read from the JSON config file.
  • The class can have an enum ParamId object that relates the parameter indices to their names. This makes it easier to reference the parameters while implementing the governing equations of the block (discussed below). The order of parameters in the ParamId object should match the order in the constructor.
  • The class should have setup_dofs and update_constant functions. It should also have a TripletsContributions num_triplets{?, ?, ?} object that specifies how many elements the governing equations of the block contribute to the F, E and dCdy matrices respectively. The class may also contain update_time and update_solution functions. Details are below.

3. Now implement the governing equations for the block.

  • The local state vector for each block is always arranged as [P_in, Q_in, P_out, Q_out, InternalVariable1, ..., InternalVariableN]. Here, InternalVariable* refers to any variable in the governing equations that are not in the inlet and outlet flow and pressure.
  • The equations should be written in the form E(t)*ydot + F(t)*y + c(y,t) = 0. Here, y is the local state vector mentioned above, ydot is the time-derivative of the state vector, E and F are matrices and c is a vector containing all non-linear and constant terms in the equation. E and F are size (number_of_equations*size_of_state_vector), while c is length number_of_equations.
  • All matrix elements that are constant are specified in update_constant. Matrix elements that depend only on time (not the solution of the problem itself) are specified in update_time. Matrix elements that change with the solution (i.e. depend on the state variables themselves) are specified in update_solution. Not all blocks will require the latter two functions.
  • The elements of the E and F matrix are populated using the syntax system.F.coeffRef(global_eqn_ids[current_block_equation_id], global_var_ids[current_block_variable_ids]) = A. Here, current_block_equation_id goes from 0 to number_of_equations (for the current block) and current_block_variable_ids goes from 0 to size_of_state_vector for the current block. The indices correspond to the block's local state vector mentioned above.
  • If the block contains non-linear equations, these terms must be specified in update_solution as system.C(global_eqn_ids[current_block_equation_id]) = non_linear_term.
  • For non-linear equations, the derivative of the term specified above with respect to each state variable must also be provided. This goes into a dC_dy matrix using the following syntax system.dC_dy.coeffRef(global_eqn_ids[current_block_equation_id], global_var_ids[current_block_variable_id]) = A, where A is the derivative of the non-linear term in the equation with ID current_block_equation_id with respect to the local state variable with ID current_block_variable_id'. For example, if the non-linear term is in the first equation, current_block_equation_id = 0. For the derivative of this term with respect to P_in, current_block_variable_id = 0and for the derivative of this term with respect toP_out, current_block_variable_id = 2`.

4. Add the new files (GenericBlock.h and GenericBlock.cpp) to src/model/CMakeLists.txt

This looks quite daunting when listed out like this. We need a way to explain this better before putting it into the documentation.

@kharold23
Copy link
Contributor

I just followed those steps to implement a chamber block and I got it working, so they're definitely helpful instructions! Some changes I think would be helpful: clarify that BlockClass is also in BlockType.h and then just format the instructions so they're broken up more. I think the most daunting thing about the instructions is that it looks like a lot of words. Maybe more bullet points with subsections/indenting to help group things would make things easier to follow?

@menon-karthik menon-karthik added the documentation Improvements or additions to documentation label Dec 2, 2023
@menon-karthik
Copy link
Member Author

Just noting so we don't forget - this should be added to the Simvascular documentation when it's done.

@ktbolt
Copy link
Contributor

ktbolt commented Dec 2, 2023

@menon-karthik Do you mean just briefly mention that it is possible to add new blocks in the SimVascular documentation and adding a link to the GitHub documentation?

The add new blocks documentation is related to the solver implementation so it should just be placed in GitHub.

@mrp089 What do you think?

@menon-karthik
Copy link
Member Author

@menon-karthik Do you mean just briefly mention that it is possible to add new blocks in the SimVascular documentation and adding a link to the GitHub documentation?

Yup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants