Skip to content

Commit

Permalink
Upgrading macOS in Github Actions (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
menon-karthik authored Jul 10, 2024
1 parent 688cece commit 2b7b80d
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 55 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, macos-11, macos-12]
os: [ubuntu-22.04, ubuntu-latest, macos-13, macos-latest]
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: conda-incubator/setup-miniconda@v3
with:
auto-update-conda: true
- name: Install ubuntu dependencies
if: startsWith(matrix.os, 'ubuntu')
run: sudo apt update && sudo apt install build-essential cmake lcov
Expand Down
1 change: 1 addition & 0 deletions docs/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_BIB_STYLE = plain
FORMULA_FONTSIZE = 15
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
Expand Down
158 changes: 158 additions & 0 deletions docs/pages/add_block.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
@page add_block Adding New Blocks

[TOC]

Below are details on the steps required to implement a new block in svZeroDSolver.

*Note: The best way to implement a new block is to look at examples of existing block classes. See the `ValveTanh` class for an example.*

## 1. Name the new block.

* The name should then be added to the following lists/dictionaries:
* `BlockType` in src/model/BlockType.h
* `block_factory_map` in src/model/Model.cpp
* *Note: In `block_factory_map`, the dictionary key should match the string specifying the type of block in the `.json` configuration/input file, and the dictionary value should match the class constructor name for the block.*
* If the new block requires special handling that is different from the current blocks (most new blocks do not), add a new category to `BlockClass` in src/model/BlockType.h

<p> <br> </p>

## 2. Create a class for the new block.

### Class constructor

* The new class will be inherited from `Block`. Define a constructor of the form:
```
MyNewBlock(int id, Model *model)
: Block(id, model, BlockType::block_type, BlockClass::block_class,
{{Param_1, InputParameter()},
{Param_2, InputParameter()},
...,
{Param_N, InputParameter()}}) {}
```
* `MyNewBlock` is the name of the new class
* `block_type` and `block_class` are the same as what was added in Step 1 above.
* The names of the input parameters of the block are `Param_1`, ... , `Param_N`.
* The properties of each parameter are defined by `InputParameter`, which specifies whether it is optional, an array, a scalar, a function, and its default value.
* The names `Param_1`, ... , `Param_N` must be the same as the parameter names within the block definition in the `.json` configuration/input file.

<p> <br> </p>

### Set up the degrees of freedom
* The class must have a `setup_dofs(DOFHandler &dofhandler)` function.
* This function typically only includes a call to the following function:
```
Block::setup_dofs_(DOFHandler &dofhandler, int num_equations, const std::list<std::string> &internal_var_names)
```
* In the above function, `num_equations` is the number of governing equations for the new block.
* `internal_var_names` is a list of strings that specify names for variables that are internal to the block, i.e. all variables for the block apart from the flow and pressure at the block's inlets and outlets.

<p> <br> </p>

### Other class members

* The class should have a `TripletsContributions num_triplets{*, *, *}` object.
* This specifies how many elements the governing equations of the block contribute to the global `F`, `E` and `dC_dy` matrices respectively.
* Details are in Step 3 below.

<p> <br> </p>

* The class should have an `update_constant` function and may also contain `update_time` and `update_solution` functions. These functions implement the governing equations for the block. Details are in Steps 3-4 below.

<p> <br> </p>

* *Optional:* 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.

<p> <br> </p>

## 3. Set up the governing equations for the block.

### State vector

* The local state vector for each block is always arranged as `y = [P_in, Q_in, P_out, Q_out, InternalVariable_1, ..., InternalVariable_N]`.
* Here, `InternalVariable*` refers to any variable in the governing equations that are not the inlet and outlet flow and pressure. These are the same as those discussed above in the function `setup_dofs`.
* The corresponding time-derivative of this state vector is `ydot = dP_in/dt, dQ_in/dt, ...]`.
* *Note: The length of the state vector is typically four (inlet and outlet pressure and flow) plus the number of internal variables.*

<p> <br> </p>

### Governing equations

* The equations should be written in the form `E(t)*ydot + F(t)*y + C(y,ydot,t) = 0`.
* `y` is the local state vector mentioned above.
* `ydot` is the time-derivative of the local state vector.
* `E` and `F` are matrices of size `number_of_equations*size_of_state_vector`.
* `C` is a vector of length `number_of_equations`.
* `E` and `F` contain terms of the governing equation that multiply the respective components of `ydot` and `y` respectively.
* `C` contains all non-linear and constant terms in the equation.
* If the equation contains non-linear terms, the developer should also write out the derivative of `C` with respect to `y` and `ydot`. These will be stored in the block's `dC_dy` and `dC_dydot` matrices, both of which are size `number_of_equations*size_of_state_vector`.

<p> <br> </p>

### An example

* Assume a block has the following non-linear governing equations:

\f$ a \frac{dQ_{in}}{dt} + b P_{in} + c \frac{dP_{in}}{dt} Q_{in} + d = 0 \f$

\f$ e \frac{dP_{out}}{dt} + f Q_{out} Q_{out} + g P_{out} + h I_{1} = 0 \f$

* For this block, \f$P_{in}\f$ and \f$Q_{in}\f$ are the pressure and flow at the inlet respectively, \f$P_{out}\f$ and \f$Q_{out}\f$ are the pressure and flow at the outlet, and \f$I_{1}\f$ is an internal variable.
* The state vector is \f$[P_{in}, Q_{in}, P_{out}, Q_{out}, I_{1}]\f$.
* The contributions to the local `F` matrix are `F[0,0] = b`, `F[1,2] = g` and `F[1,4] = h`.
* The contributions to the local `E` matrix are `E[0,1] = a` and `E[1,2] = e`.
* The contributions to the local `C` vector are `C[0] = c*(dP_in/dt)*Q_in + d` and `C[1] = f*Q_out*Q_out`.
* The contributions to the local `dC_dy` matrix are `dC_dy[0,1] = c*(dP_in/dt)` and `dC_dy[1,3] = 2*f*Q_out`.
* The contributions to the local `dC_dydot` matrix are `dC_dydot[0,0] = c*Q_in`.
* In this case, the block has 3 contributions to `F`, 2 contributions to `E`, and 2 constributions to `dC_dy`. So the class will have a member `TripletsContributions num_triplets{3, 2, 2}`.

<p> <br> </p>

## 4. Implement the matrix equations for the block.

* Implement the `update_constant`, `update_time` and `update_solution` functions.
* All matrix elements that are constant must be specified in `update_constant`.
* Matrix elements that depend only on time (not the state variables) must be specified in `update_time`.
* Matrix elements that change with the solution (i.e. depend on the state variables themselves) must be specified in `update_solution`.
* *Note: Not all blocks will require the `update_time` and `update_solution` functions.*

<p> <br> </p>

### Implementation details

* The elements of the matrices `E`, `F`, `dC_dy` and `dC_dydot` are populated using the following 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-1` (for the current block) and `current_block_variable_ids` goes from 0 to `size_of_state_vector-1` for the current block.

<p> <br> </p>

* If the governing equations contain non-linear terms, these terms must be specified in `update_solution` as:
```
system.C(global_eqn_ids[current_block_equation_id]) = non_linear_term
```

<p> <br> </p>

* For non-linear equations, the derivative of the terms in `C` with respect to each state variable `y` and `ydot` must also be provided. These go into `dC_dy` and `dC_dydot` matrices.
* A `dC_dy` matrix contribution can be specified using the following syntax:
```
system.dC_dy.coeffRef(global_eqn_ids[current_block_equation_id], global_var_ids[current_block_variable_id]) = a
```
* Here, `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, then `current_block_equation_id = 0`.
* For the derivative of this term with respect to `P_in`, set `current_block_variable_id = 0`, and for the derivative of this term with respect to `P_out`, set `current_block_variable_id = 2`.
* The same indexing applies to derivatives with respect to the `ydot` state variables, i.e. for the derivative of the term with respect to `dP_in/dt`, set `current_block_variable_id = 0`.

<p> <br> </p>

* *Note: Any matrix and vector components that are not specified are 0 by default.*

<p> <br> </p>

## 4. Add the new block to the build system.

* Add `MyNewBlock.h` and `MyNewBlock.cpp` to `src/model/CMakeLists.txt`

44 changes: 30 additions & 14 deletions docs/pages/developer_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,8 @@ of svZeroDSolver, namely:
* svZerodSolver in `svzerodsolver.cpp`
* Python API in `pysvzerod.cpp`

The header-based library in the `src` folder contains classes and functions that are collectively used by
all applications. A good overview over the general architecture can be found in the
<a href="namespaces.html">list of namespaces</a>.


## Build in debug mode
# Build in debug mode

For debug purposes it is recommended to build svZeroDSolver in Debug mode.

Expand All @@ -30,15 +26,35 @@ cmake -DCMAKE_BUILD_TYPE=Debug ..
cmake --build .
```

## Install with pip
# Install with pip

Execute this command in the root folder to install the current source:
```bash
pip install -e ".[dev]"
```
This is useful when continuously running the integration tests during development.

## Code Style
# Contributing to svZeroDSolver

**NOTE: To contribute new developments to the main branch of svZeroDSolver, developers must first open an issue on the svZeroDSolver Github repository to describe the planned changes.**

* The changes should be implemented in a feature branch of the developer's fork of svZeroDSolver.
* Once the changes are implemented, the developer should make sure the build, documentation, and code format tests are passing on the user's feature branch.
* The tests are automatically run when pushing changes to the developer's remote branch on Github.
* Alternatively, the developer can run the tests locally.
* The build tests can be run using the `pip` install and `pytest`.
* The tests for the C++ interface require the `CMake` install and can be run by building the tests in `svZeroDSolver/tests/test_interface`.
* Code formatting can be performed following the instructions in the Formatting section below.
* The documentation can be built following the instructions in the Documentation section below.
* Once all the tests are passing, the developer should open a pull request from the feature branch and link the relevant issue.

# Adding new blocks

The modular architecture of svZeroDSolver relies on "blocks", such as blood vessels, junctions, valves, boundary conditions, etc. These blocks are assembled in a manner specified by the `.json` configuration file, which dictates the assembled governing equations for the model. We are always interested in adding new blocks to expand the funcitonality of svZeroDSolver.

Detailed steps required to implement a new block in svZeroDSolver are available [here](@ref add_block).

# Code Style

We follow the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html).

Expand Down Expand Up @@ -81,7 +97,7 @@ requirements.

On Sherlock at Stanford, clang-format is included in the `llvm` module.

## Documentation
# Documentation

We use [Doxygen](https://doxygen.nl) to automatically build an html documentation
from source code. Please have at Doxygen's [Documentation Guide](https://www.doxygen.nl/manual/docblocks.html)
Expand All @@ -95,20 +111,20 @@ and cannot be merged.**
In the following you can find a short recap of the most important
commands:

### Latex equations
## Latex equations
For inline equations use `\f$a+b=c\f$` and for block equations use:
```
\f[
a+b=c
\f]
```

### Citations
## Citations
If you want to cite a piece literature in your documentation, add
a respective BibTeX citation to `docs/cpp/references.bib` and use `\cite name_of_citation` to
a respective BibTeX citation to `docs/references.bib` and use `\cite name_of_citation` to
cite the document.

### Drawing circuits
## Drawing circuits
As the elements of the svZeroDSolver are often represented
in the form of electrical circuits, we use [CircuiTikZ](https://ctan.org/pkg/circuitikz?lang=en)
to draw circuits in the documentation (see blocks in Block for examples).
Expand All @@ -121,7 +137,7 @@ To start a CircuitTikZ drawing use the following command:
\f]
```

### Build
## Build
The documentation is automatically built in the GitHub CI/CD and published
on GitHub pages. If you want to build the documentation locally, you can use:

Expand All @@ -133,7 +149,7 @@ You can then view the documentation locally in your browser by opening `docs/bui
If you do not have Doxygen install you can do that with `brew install doxygen`
on macOS or with `sudo apt-get install doxygen` on Linux.

## Profiling
# Profiling

Profiling helps to easily identify bottlenecks in the code. A profiling report
lists the executation time spend on certain parts of the code. If you experience
Expand Down
Loading

0 comments on commit 2b7b80d

Please sign in to comment.