├── .clang-format
├── .gitignore
├── .gitmodules
├── .style.yapf
├── CMakeLists.txt
├── Doxyfile
├── LICENSE
├── README.md
├── ci
├── Dockerfile
└── Jenkinsfile
├── cmake
└── FindSTKFMM.cmake
├── docs
├── Makefile
├── make.bat
└── source
│ ├── _static
│ └── example.mp4
│ ├── conf.py
│ ├── config_generation.rst
│ ├── getting_started.rst
│ ├── images
│ ├── SkellySim_Logo_RGB_Full.png
│ ├── example_ic.png
│ └── example_plots.png
│ ├── index.rst
│ ├── installation.rst
│ ├── postprocessing.rst
│ └── visualization.rst
├── examples
├── analysis_example.py
├── ellipsoid
│ └── gen_config.py
├── listener_mode
│ ├── README.md
│ ├── gen_config.py
│ └── listener_example.py
├── oocyte
│ └── gen_config.py
├── skelly_sim_slurm_sbatch.sh
└── stokes_tests
│ ├── body_const_force
│ ├── calculate_velocity_error.py
│ └── gen_config.py
│ ├── body_const_force_in_sphere
│ ├── gen_config.py
│ └── velocity_vs_z.py
│ ├── clamped_buckling
│ ├── README.md
│ ├── hopf-bifurcation.pdf
│ ├── plot_deflection.py
│ ├── sigma72.png
│ ├── sigma72
│ │ └── gen_config.py
│ ├── sigma80.png
│ └── sigma80
│ │ └── gen_config.py
│ ├── fiber_const_force
│ ├── calc_velocity.py
│ └── gen_config.py
│ ├── fiber_const_force_in_sphere
│ ├── README.md
│ ├── calc_velocity.py
│ └── gen_config.py
│ ├── ps_const_force_in_sphere
│ ├── README.md
│ ├── analysis_example.py
│ └── gen_config.py
│ └── ps_const_torque_in_sphere
│ ├── README.md
│ ├── analysis_example.py
│ └── gen_config.py
├── include
├── background_source.hpp
├── body.hpp
├── body_container.hpp
├── body_deformable.hpp
├── body_ellipsoidal.hpp
├── body_spherical.hpp
├── cnpy.hpp
├── eigen_matrix_plugin.h
├── eigen_quaternion_plugin.h
├── fiber_chebyshev_penalty_autodiff.hpp
├── fiber_chebyshev_penalty_autodiff_external.hpp
├── fiber_container_base.hpp
├── fiber_container_finite_difference.hpp
├── fiber_finite_difference.hpp
├── fiber_state.hpp
├── io_maps.hpp
├── kernels.hpp
├── listener.hpp
├── params.hpp
├── parse_util.hpp
├── periphery.hpp
├── point_source.hpp
├── rng.hpp
├── serialization.hpp
├── skelly_chebyshev.hpp
├── skelly_fiber.hpp
├── skelly_sim.hpp
├── solver.hpp
├── solver_hydro.hpp
├── streamline.hpp
├── system.hpp
├── trajectory_reader.hpp
└── utils.hpp
├── misc
├── logos
│ ├── CMYK
│ │ ├── Icon
│ │ │ ├── EPS
│ │ │ │ ├── SkellySim_Icon_CMYK_Black.eps
│ │ │ │ ├── SkellySim_Icon_CMYK_Full.eps
│ │ │ │ └── SkellySim_Icon_CMYK_White.eps
│ │ │ ├── JPG
│ │ │ │ ├── SkellySim_Icon_CMYK_Black.jpg
│ │ │ │ └── SkellySim_Icon_CMYK_Full.jpg
│ │ │ ├── PDF
│ │ │ │ ├── SkellySim_Icon_CMYK_Black.pdf
│ │ │ │ └── SkellySim_Icon_CMYK_Full.pdf
│ │ │ ├── PNG
│ │ │ │ ├── SkellySim_Icon_CMYK_Black.png
│ │ │ │ ├── SkellySim_Icon_CMYK_Full.png
│ │ │ │ └── SkellySim_Icon_CMYK_White.png
│ │ │ └── SVG
│ │ │ │ ├── SkellySim_Icon_CMYK_Black.svg
│ │ │ │ ├── SkellySim_Icon_CMYK_Full.svg
│ │ │ │ └── SkellySim_Icon_CMYK_White.svg
│ │ └── Logo
│ │ │ ├── EPS
│ │ │ ├── SkellySim_Logo_CMYK_Black.eps
│ │ │ ├── SkellySim_Logo_CMYK_Full.eps
│ │ │ └── SkellySim_Logo_CMYK_White.eps
│ │ │ ├── JPG
│ │ │ ├── SkellySim_Logo_CMYK_Black.jpg
│ │ │ └── SkellySim_Logo_CMYK_Full.jpg
│ │ │ ├── PDF
│ │ │ ├── SkellySim_Logo_CMYK_Black.pdf
│ │ │ └── SkellySim_Logo_CMYK_Full.pdf
│ │ │ ├── PNG
│ │ │ ├── SkellySim_Logo_CMYK_Black.png
│ │ │ ├── SkellySim_Logo_CMYK_Full.png
│ │ │ └── SkellySim_Logo_CMYK_White.png
│ │ │ └── SVG
│ │ │ ├── SkellySim_Logo_CMYK_Black.svg
│ │ │ ├── SkellySim_Logo_CMYK_Full.svg
│ │ │ └── SkellySim_Logo_CMYK_White.svg
│ ├── Favicon
│ │ └── SkellySim_Favicon.ico
│ └── RGB
│ │ ├── Icon
│ │ ├── EPS
│ │ │ ├── SkellySim_Icon_RGB_Black.eps
│ │ │ ├── SkellySim_Icon_RGB_Full.eps
│ │ │ └── SkellySim_Icon_RGB_White.eps
│ │ ├── JPG
│ │ │ ├── SkellySim_Icon_RGB_Black.jpg
│ │ │ └── SkellySim_Icon_RGB_Full.jpg
│ │ ├── PDF
│ │ │ ├── SkellySim_Icon_RGB_Black.pdf
│ │ │ └── SkellySim_Icon_RGB_Full.pdf
│ │ ├── PNG
│ │ │ ├── SkellySim_Icon_RGB_Black.png
│ │ │ ├── SkellySim_Icon_RGB_Full.png
│ │ │ └── SkellySim_Icon_RGB_White.png
│ │ └── SVG
│ │ │ ├── SkellySim_Icon_RGB_Black.svg
│ │ │ ├── SkellySim_Icon_RGB_Full.svg
│ │ │ └── SkellySim_Icon_RGB_White.svg
│ │ └── Logo
│ │ ├── EPS
│ │ ├── SkellySim_Logo_RGB_Black.eps
│ │ ├── SkellySim_Logo_RGB_Full.eps
│ │ └── SkellySim_Logo_RGB_White.eps
│ │ ├── JPG
│ │ ├── SkellySim_Logo_RGB_Black.jpg
│ │ └── SkellySim_Logo_RGB_Full.jpg
│ │ ├── PDF
│ │ ├── SkellySim_Logo_RGB_Black.pdf
│ │ └── SkellySim_Logo_RGB_Full.pdf
│ │ ├── PNG
│ │ ├── SkellySim_Logo_RGB_Black.png
│ │ ├── SkellySim_Logo_RGB_Full.png
│ │ └── SkellySim_Logo_RGB_White.png
│ │ └── SVG
│ │ ├── SkellySim_Logo_RGB_Black.svg
│ │ ├── SkellySim_Logo_RGB_Full.svg
│ │ └── SkellySim_Logo_RGB_White.svg
└── module_template.in
├── notes
├── model_changes.md
├── model_overview.pdf
├── model_overview.tex
├── planning.md
└── skelly_sim_installation.md
├── pyproject.toml
├── scripts
├── skelly.blend
├── skelly_blend.py
└── skelly_sim.def
├── src
├── core
│ ├── background_source.cpp
│ ├── body.cpp
│ ├── body_container.cpp
│ ├── body_deformable.cpp
│ ├── body_ellipsoidal.cpp
│ ├── body_spherical.cpp
│ ├── cnpy.cpp
│ ├── dynamic_instability.cpp
│ ├── fiber_container_base.cpp
│ ├── fiber_container_finite_difference.cpp
│ ├── fiber_finite_difference.cpp
│ ├── kernels.cpp
│ ├── kernels.cu
│ ├── listener.cpp
│ ├── params.cpp
│ ├── periphery.cpp
│ ├── point_source.cpp
│ ├── rng.cpp
│ ├── solver_hydro.cpp
│ ├── streamline.cpp
│ ├── system.cpp
│ ├── trajectory_reader.cpp
│ └── utils.cpp
├── skelly_sim.cpp
└── skelly_sim
│ ├── Smooth_Closed_Surface_Quadrature_RBF.py
│ ├── __init__.py
│ ├── kernels.py
│ ├── param_tools.py
│ ├── paraview_utils
│ ├── __init__.py
│ ├── body_reader.py
│ ├── body_reader_request.py
│ ├── fiber_reader.py
│ ├── fiber_reader_request.py
│ ├── field_reader.py
│ ├── field_reader_request.py
│ └── trajectory_utility.py
│ ├── periphery.py
│ ├── precompute.py
│ ├── quaternion.py
│ ├── reader.py
│ ├── shape_gallery.py
│ ├── skelly_config.py
│ └── testing.py
└── tests
├── combined
├── bodies
│ ├── test_compare_quadrature.py
│ ├── test_ellipsoid_assphere_constforce.py
│ ├── test_ellipsoid_oblate_z_force.py
│ ├── test_ellipsoid_prolate_z_force.py
│ └── test_ellipsoid_prolate_z_torque.py
├── regression_tests
│ ├── fdfiber_compression_data.npy
│ ├── fdfiber_compression_finalpositions.npz
│ └── test_body_fdfiber_compression.py
├── test_body_const_force.py
├── test_body_const_torque.py
├── test_body_oscillatory_force.py
├── test_clamped_buckling_sigma72.py
├── test_clamped_buckling_sigma80.py
├── test_fiber_const_force.py
├── test_fiber_dualfilament.py
└── test_fiber_uniform_background.py
└── core
├── CMakeLists.txt
├── belos_fiberpenalty_test.cpp
├── dynamic_instability_test.cpp
├── jnewton_fiberpenalty_test.cpp
├── kernel_test.cpp
├── performance_fiberpenalty_combined.cpp
├── performance_fiberpenalty_combined_compare.cpp
├── performance_hydrodynamics_combined.cpp
├── performance_hydrodynamics_directrotation.cpp
├── solver_test.cpp
├── test_files
├── body_precompute_body_ellipsoid_n1.npz
├── ellipsoidal_body_n1.toml
├── fiber_container_fdf_n1.toml
└── fiber_container_fdf_n10.toml
└── unit_tests
├── CMakeLists.txt
├── julia_fiber_penalty_results.hpp
├── mpi_environment.hpp
├── unit_test_body_ellipsoidal.cpp
├── unit_test_fiber_chebyshev_penalty_autodiff.cpp
├── unit_test_fiber_finite_difference.cpp
├── unit_test_fibercontainer_finite_difference.cpp
├── unit_test_fibercontainer_finite_difference_mpi.cpp
├── unit_test_serialization.cpp
├── unit_test_serialization_mpi.cpp
└── unit_test_skelly_chebyshev.cpp
/.clang-format:
--------------------------------------------------------------------------------
1 | ---
2 | Language: Cpp
3 | BasedOnStyle: LLVM
4 | TabWidth: 4
5 | ColumnLimit: 120
6 | IndentWidth: 4
7 | AlwaysBreakTemplateDeclarations: true
8 | ...
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /*.pyc
3 | *.out
4 | *.vf
5 | /examples/**/*.npz
6 | /examples/**/*.toml
7 | /examples/**/*.*_config
8 | /examples/**/*.cindex
9 | /examples/**/*.index
10 |
11 | _region_.*
12 | notes/*.aux
13 | notes/*.log
14 |
15 | /skelly_sim.egg-info
16 | __pycache__
17 | *.blend1
18 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "extern/spdlog"]
2 | path = extern/spdlog
3 | url = https://github.com/gabime/spdlog.git
4 | [submodule "extern/trng4"]
5 | path = extern/trng4
6 | url = https://github.com/blackwer/trng4
7 | [submodule "extern/toml11"]
8 | path = extern/toml11
9 | url = https://github.com/ToruNiina/toml11
10 | [submodule "extern/msgpack-c"]
11 | path = extern/msgpack-c
12 | url = https://github.com/msgpack/msgpack-c
13 | [submodule "extern/eigen"]
14 | path = extern/eigen
15 | url = https://gitlab.com/libeigen/eigen
16 | [submodule "extern/autodiff"]
17 | path = extern/autodiff
18 | url = https://github.com/autodiff/autodiff.git
19 |
--------------------------------------------------------------------------------
/.style.yapf:
--------------------------------------------------------------------------------
1 | [style]
2 | based_on_style = pep8
3 | column_limit = 120
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | SkellySim is a simulation package for simulating cellular components such as flexible filaments, motor proteins, and arbitrary rigid bodies.
4 | It's designed to be highly scalable, capable of both OpenMP and MPI style parallelism, while using the efficient STKFMM/PVFMM libraries for hydrodynamic resolution.
5 |
6 | # Documentation
7 | Flatiron users should read [Installation/Running at FI first](#installation-running-at-fi),
8 | since users there should not have to install the `C++` portion themselves.
9 |
10 | [SkellySim documentation](https://users.flatironinstitute.org/~rblackwell/py-skellysim)
11 |
12 | # Installation/Running at FI
13 | ## Basic setup
14 | There are (currently) two components to skelly_sim, the python portion, and the actual
15 | binary. The python portion is mostly for visualization, as well as generating config files and
16 | precompute data. The binary (C++) portion is for actually running the simulation.
17 |
18 | To install the python portion (in your virtual environment, conda environment, or using the `pip3 --user` option). For a virtualenv
19 | ```bash
20 | module load python
21 | python3 -m venv /path/to/my/virtualenv
22 | source /path/to/my/virtualenv/bin/activate
23 | pip3 install git+https://github.com/flatironinstitute/SkellySim
24 | ```
25 | or for a conda environment
26 | ```
27 | conda create -n myenvname
28 | conda activate myenvname
29 | pip3 install git+https://github.com/flatironinstitute/SkellySim
30 | ```
31 | Due to the complex dependencies of the C++ portion, until I finish packaging things, you can use my modules.
32 | ```bash
33 | module -q purge
34 | # REMOVE python module from this if you are using conda!!!!
35 | module use ~rblackwell/modules
36 | module -q load gcc/11 openmpi python trilinos pvfmm/1.3.0 intel-oneapi-mkl cuda flexiblas skelly_sim
37 | ```
38 |
39 | ## Building from source at FI (developers or externs)
40 |
41 |
42 | ```bash
43 | module -q purge
44 | module -q load gcc/11 openmpi python cmake trilinos pvfmm stkfmm intel-oneapi-mkl cuda boost flexiblas
45 |
46 | git clone https://github.com/flatironinstitute/SkellySim
47 | cd SkellySim
48 | git submodule update --init --recursive
49 | mkdir -p build
50 | cd build
51 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS=-march=broadwell -DCMAKE_CUDA_ARCHITECTURES="70;75;86;90" -DBLA_VENDOR=FlexiBLAS
52 | make -j$((2*$(nproc)))
53 | ```
54 |
--------------------------------------------------------------------------------
/ci/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nvidia/cuda:11.7.1-cudnn8-devel-ubuntu22.04
2 |
3 | ENV MKLROOT=/opt/intel/oneapi/mkl/latest
4 | ENV STKFMM_ROOT=/usr/local
5 | ENV CXXFLAGS_="-I/usr/lib/x86_64-linux-gnu/openmpi/include -march=broadwell -g"
6 | ENV PVFMM_DIR="/usr/local/share/pvfmm"
7 |
8 | RUN apt -y update && \
9 | apt -y install curl gpg software-properties-common && \
10 | curl -s https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor > /usr/share/keyrings/oneapi-archive-keyring.gpg && \
11 | echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" > /etc/apt/sources.list.d/oneAPI.list && \
12 | apt-add-repository multiverse && \
13 | apt-add-repository universe && \
14 | apt -y update && \
15 | apt -y install libboost-dev intel-oneapi-mkl-devel build-essential libopenmpi-dev \
16 | cmake libeigen3-dev ninja-build git python3-pip && \
17 | apt -y autoclean && \
18 | rm -rf /var/cache/apt/* /var/lib/apt/lists/*
19 |
20 | RUN mkdir build
21 |
22 | RUN cd build && \
23 | git clone https://github.com/dmalhotra/pvfmm && \
24 | cd pvfmm && \
25 | git checkout v1.3.0 && \
26 | git submodule update --init && \
27 | mkdir build && \
28 | cd build && \
29 | cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release \
30 | -DCMAKE_CXX_FLAGS="$CXXFLAGS_" -GNinja && \
31 | ninja && \
32 | ninja install
33 |
34 | RUN cd build && \
35 | git clone https://github.com/wenyan4work/STKFMM && \
36 | cd STKFMM && \
37 | mkdir build && \
38 | cd build && \
39 | pvfmm_DIR=/usr/local/share/pvfmm cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local \
40 | -DCMAKE_CXX_FLAGS="$CXXFLAGS_" -GNinja && \
41 | ninja && \
42 | ninja install
43 |
44 | RUN cd build && \
45 | curl -L -o trilinos.tar.gz https://github.com/trilinos/Trilinos/archive/refs/tags/trilinos-release-13-2-0.tar.gz && \
46 | tar -xf trilinos.tar.gz && \
47 | cd Trilinos-trilinos-release-13-2-0 && \
48 | mkdir build && \
49 | cd build && \
50 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local \
51 | -DTrilinos_VERBOSE_CONFIGURE:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=ON -DCMAKE_CXX_STANDARD:STRING=14 \
52 | -DTrilinos_ENABLE_ALL_OPTIONAL_PACKAGES:BOOL=OFF -DTrilinos_ENABLE_ALL_PACKAGES:BOOL=OFF \
53 | -DTrilinos_ENABLE_OpenMP=ON -DTrilinos_ENABLE_Kokkos:BOOL=ON -DTrilinos_ENABLE_Belos:BOOL=ON \
54 | -DTrilinos_ENABLE_Tpetra:BOOL=ON -DTrilinos_ENABLE_Teuchos:BOOL=ON -DBUILD_TESTING:BOOL=OFF \
55 | -DTrilinos_ENABLE_Fortran:BOOL=OFF -D TPL_ENABLE_MPI=ON \
56 | -DTPL_BLAS_LIBRARIES=/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_rt.so \
57 | -DTPL_LAPACK_LIBRARIES=/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_rt.so \
58 | -DCMAKE_CXX_FLAGS="$CXXFLAGS_" -GNinja && \
59 | ninja && \
60 | ninja install
61 |
62 | RUN rm -rf build
63 |
64 | RUN mkdir -p $PVFMM_DIR && \
65 | cd $PVFMM_DIR && \
66 | curl -o Precomp_stokes_PVel_m16.data https://users.flatironinstitute.org/~rblackwell/pvfmm/Precomp_stokes_PVel_m16.data && \
67 | curl -o Precomp_stokes_vel_m16.data https://users.flatironinstitute.org/~rblackwell/pvfmm/Precomp_stokes_vel_m16.data
68 |
--------------------------------------------------------------------------------
/ci/Jenkinsfile:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent none
3 | options {
4 | disableConcurrentBuilds()
5 | buildDiscarder(logRotator(numToKeepStr: '8', daysToKeepStr: '20'))
6 | timeout(time: 1, unit: 'HOURS')
7 | }
8 | stages {
9 | stage('main') {
10 | agent {
11 | dockerfile {
12 | dir 'ci'
13 | args '--gpus 1'
14 | label 'v100'
15 | }
16 | }
17 | environment {
18 | HOME = "${WORKSPACE}"
19 | PYPATH = "${WORKSPACE}/.local/bin"
20 | LC_ALL = "C"
21 | MKL_THREADING_LAYER = "GNU"
22 | PVFMM_DIR = "/usr/local/share/pvfmm"
23 | LD_LIBRARY_PATH = "/opt/intel/oneapi/mkl/latest/lib/intel64:/usr/local/lib"
24 | OMP_NUM_THREADS = "4"
25 | }
26 | steps {
27 | sh 'printenv | egrep "(^HOME|^PATH|^STKFMM_ROOT|^MKLROOT|^LD_LIBRARY_PATH|^pvfmm_DIR|^MKL_THREADING_LAYER|^LC_ALL|^CXX_FLAGS_|^PVFMM_DIR|^OMP_NUM_THREADS)"'
28 | sh 'python3 -m pip install --user ".[tests]"'
29 | sh 'git submodule update --init'
30 | sh 'cmake -B build . -DCMAKE_BUILD_TYPE=Release \
31 | -DCMAKE_CXX_FLAGS="$CXXFLAGS_" -DBLAS_LIBRARIES=$MKLROOT/lib/intel64/libmkl_rt.so \
32 | -DLAPACK_LIBRARIES=$MKLROOT/lib/intel64/libmkl_rt.so -DCMAKE_CUDA_ARCHITECTURES="70" -GNinja'
33 | sh 'ninja -C build -j $OMP_NUM_THREADS'
34 | sh 'cd build && ctest --output-on-failure'
35 | sh 'PATH=$PYPATH:$PWD/build:$PATH python3 -m pytest tests --verbose'
36 | }
37 | }
38 | }
39 | post {
40 | failure {
41 | emailext subject: '$PROJECT_NAME - Build #$BUILD_NUMBER - $BUILD_STATUS',
42 | body: '''$PROJECT_NAME - Build #$BUILD_NUMBER - $BUILD_STATUS
43 | Check console output at $BUILD_URL to view full results.
44 | Building $BRANCH_NAME for $CAUSE
45 | $JOB_DESCRIPTION
46 | Changes:
47 | $CHANGES
48 | End of build log:
49 | ${BUILD_LOG,maxLines=200}
50 | ''',
51 | recipientProviders: [
52 | [$class: 'DevelopersRecipientProvider'],
53 | ],
54 | replyTo: '$DEFAULT_REPLYTO',
55 | to: 'rblackwell@flatironinstitute.org'
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/cmake/FindSTKFMM.cmake:
--------------------------------------------------------------------------------
1 | # Find the STKFMM Library
2 | #
3 | # STKFMM_FOUND - System has MKL
4 | # STKFMM_INCLUDE_DIRS - MKL include files directories
5 | # STKFMM_LIBRARIES - The MKL libraries
6 | # STKFMM_INTERFACE_LIBRARY - MKL interface library
7 | # STKFMM_SEQUENTIAL_LAYER_LIBRARY - MKL sequential layer library
8 | # STKFMM_CORE_LIBRARY - MKL core library
9 | #
10 | # Example usage:
11 | #
12 | # find_package(STKFMM)
13 | # if(STKFMM_FOUND)
14 | # target_link_libraries(TARGET ${STKFMM_LIBRARIES})
15 | # endif()
16 |
17 | # If already in cache, be silent
18 | if (STKFMM_INCLUDE_DIRS AND STKFMM_LIBRARIES)
19 | set (STKFMM_FIND_QUIETLY TRUE)
20 | endif()
21 |
22 | find_path(STKFMM_INCLUDE_DIR NAMES STKFMM/STKFMM.h HINTS $ENV{STKFMM_ROOT}/include $ENV{STKFMM_BASE}/include)
23 |
24 | find_library(STKFMM_LIBRARY
25 | NAMES STKFMM_STATIC
26 | PATHS $ENV{STKFMM_ROOT}/lib64
27 | $ENV{STKFMM_ROOT}/lib
28 | $ENV{STKFMM_BASE}/lib64
29 | $ENV{STKFMM_BASE}/lib
30 | NO_DEFAULT_PATH)
31 |
32 | set(STKFMM_INCLUDE_DIRS ${STKFMM_INCLUDE_DIR})
33 | set(STKFMM_LIBRARIES ${STKFMM_LIBRARY})
34 |
35 | # Handle the QUIETLY and REQUIRED arguments and set STKFMM_FOUND to TRUE if
36 | # all listed variables are TRUE.
37 | INCLUDE(FindPackageHandleStandardArgs)
38 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(STKFMM DEFAULT_MSG STKFMM_LIBRARIES STKFMM_INCLUDE_DIRS)
39 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.https://www.sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/source/_static/example.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/docs/source/_static/example.mp4
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | import os
14 | import sys
15 | sys.path.insert(0, os.path.abspath('../..'))
16 |
17 |
18 | # -- Project information -----------------------------------------------------
19 |
20 | project = 'SkellySim'
21 | copyright = '2022, Robert Blackwell, Gokberk Kabacaoglu'
22 | author = 'Robert Blackwell, Gokberk Kabacaoglu'
23 |
24 | # The full version, including alpha/beta/rc tags
25 | release = '0.9.12'
26 |
27 |
28 | # -- General configuration ---------------------------------------------------
29 |
30 | # Add any Sphinx extension module names here, as strings. They can be
31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
32 | # ones.
33 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
34 |
35 |
36 | # Add any paths that contain templates here, relative to this directory.
37 | templates_path = ['_templates']
38 |
39 | # List of patterns, relative to source directory, that match files and
40 | # directories to ignore when looking for source files.
41 | # This pattern also affects html_static_path and html_extra_path.
42 | exclude_patterns = []
43 |
44 |
45 | # -- Options for HTML output -------------------------------------------------
46 |
47 | # The theme to use for HTML and HTML Help pages. See the documentation for
48 | # a list of builtin themes.
49 | #
50 | html_theme = 'sphinx_book_theme'
51 |
52 | # Add any paths that contain custom static files (such as style sheets) here,
53 | # relative to this directory. They are copied after the builtin static files,
54 | # so a file named "default.css" will overwrite the builtin "default.css".
55 | html_static_path = ['_static']
56 |
57 |
58 | # Remove excessively long class signature from dataclass objects
59 | def process_sig(app, what, name, obj, options, signature, return_annotation):
60 | from dataclasses import is_dataclass
61 | if signature and is_dataclass(obj):
62 | signature = "()"
63 | return (signature, return_annotation)
64 |
65 | def setup(app):
66 | app.connect("autodoc-process-signature", process_sig)
67 |
68 |
69 | autodoc_mock_imports = ["function_generator"]
70 |
71 | html_theme_options = {
72 | "extra_navbar": 'SkellySim on Github',
73 | }
74 |
--------------------------------------------------------------------------------
/docs/source/images/SkellySim_Logo_RGB_Full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/docs/source/images/SkellySim_Logo_RGB_Full.png
--------------------------------------------------------------------------------
/docs/source/images/example_ic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/docs/source/images/example_ic.png
--------------------------------------------------------------------------------
/docs/source/images/example_plots.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/docs/source/images/example_plots.png
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | .. SkellySim documentation master file, created by
2 | sphinx-quickstart on Mon Mar 21 15:26:32 2022.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to SkellySim's documentation!
7 | =====================================
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: Contents:
12 |
13 | installation
14 | getting_started
15 | config_generation
16 | visualization
17 | postprocessing
18 |
19 |
20 | Indices and tables
21 | ==================
22 |
23 | * :ref:`genindex`
24 | * :ref:`modindex`
25 | * :ref:`search`
26 |
--------------------------------------------------------------------------------
/docs/source/postprocessing.rst:
--------------------------------------------------------------------------------
1 | .. _post-processing:
2 |
3 |
4 | Post-processing
5 | ---------------
6 |
7 | For post-processing that require knowledge of the velocity at arbitrary points in the fluid,
8 | SkellySim supports an "interactive" mode where it runs inside a containing python process in
9 | :code:`Listener` mode. This allows you to exploit the SkellySim machinery on demand to
10 | calculate various quantities without intermediate storage. This is extremely useful to generate
11 | your own velocity fields, stream lines, and vortex lines at any given simulation point. Please
12 | see the following to get started: `examples/listener_mode/listener_example.py
13 | `_.
14 |
15 |
16 | Trajectory format
17 | -----------------
18 |
19 | The trajectory format is a single file that consists of consecutive 'frames' with data
20 | serialized in the :code:`msgpack` format. This makes doing post-processing in your language of
21 | choice reasonably straightforward, though most existing code is centered around :code:`python`,
22 | since that's what the configuration language and visualization are implemented in. Writing
23 | readers in other languages, especially those that have builtin :code:`map` and :code:`list`
24 | types, shouldn't be too much of a problem though.
25 |
26 | Each frame of data is simply a nested dictionary of key-value pairs, which we provide a
27 | convenience wrapper class for in :obj:`Python`. There is some example usage of this class in
28 | :ref:`getting-started`, and we suggest users work from there or directly from
29 | `examples/analysis_example.py
30 | `_.
31 |
32 |
33 | TrajectoryReader class
34 | ----------------------
35 |
36 | .. autoclass:: skelly_sim.reader.TrajectoryReader
37 | :members:
38 |
39 |
40 | Listener class
41 | --------------
42 |
43 | .. autoclass:: skelly_sim.reader.Listener
44 | :members:
45 |
46 |
47 | Request class
48 | -------------
49 |
50 | .. autoclass:: skelly_sim.reader.Request
51 | :members:
52 |
53 |
54 | StreamlinesRequest class
55 | ------------------------
56 |
57 | .. autoclass:: skelly_sim.reader.StreamlinesRequest
58 | :members:
59 |
60 |
61 | VelocityFieldRequest class
62 | --------------------------
63 |
64 | .. autoclass:: skelly_sim.reader.VelocityFieldRequest
65 | :members:
66 |
--------------------------------------------------------------------------------
/docs/source/visualization.rst:
--------------------------------------------------------------------------------
1 | .. _visualization:
2 |
3 | Visualization
4 | =============
5 |
6 | There are currently two methods for visualization, and neither is anywhere near perfect.
7 |
8 | Visualization with Paraview
9 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 |
11 | Paraview provides a nice way to overlay text, view streamlines, and generally customize your
12 | visualization. Unfortunately the renderer is only OK and getting it to work with a custom
13 | python environment can be very finicky.
14 |
15 |
16 | Visualization with Blender
17 | ~~~~~~~~~~~~~~~~~~~~~~~~~~
18 |
19 | Blender is not really designed with scientific workflows in mind, but it is very flexible,
20 | plays more nicely with user python environments, has multiple renderers, all of which are
21 | better than Paraview's, and is just generally way more developer friendly. It unfortunately
22 | doesn't have many of the nice builtin-facilities of Paraview (like text overlay and
23 | streamlines), and so is therefore very much a work in progress. There is an experimental
24 | blender script `skelly_blend.py` that hopefully will become the primary way to visualize the
25 | simulation in the future. For actually doing science, Paraview is still the preferred choice,
26 | but hopefully that will change in the future.
27 |
--------------------------------------------------------------------------------
/examples/analysis_example.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib
3 | matplotlib.use('TKAgg')
4 | import matplotlib.pyplot as plt
5 |
6 | from skelly_sim.reader import TrajectoryReader, Listener, Request
7 |
8 | traj = TrajectoryReader('skelly_config.toml')
9 | shell_radius = traj.config_data['periphery']['radius']
10 | body_radius = traj.config_data['bodies'][0]['radius']
11 |
12 | body_pos = np.empty(shape=(len(traj), 3)) # COM body position in time
13 | plus_pos = np.empty(shape=(len(traj), 3)) # fiber plus end in time
14 | minus_pos = np.empty(shape=(len(traj), 3)) # fiber minus end in time
15 |
16 | for i in range(len(traj)):
17 | traj.load_frame(i)
18 | body_pos[i, :] = traj['bodies'][0]['position_']
19 | minus_pos[i, :] = traj['fibers'][0]['x_'][0, :]
20 | plus_pos[i, :] = traj['fibers'][0]['x_'][-1, :]
21 |
22 | print("system keys: " + str(list(traj.keys())))
23 | print("fiber keys: " + str(list(traj['fibers'][0].keys())))
24 | print("body keys: " + str(list(traj['bodies'][0].keys())))
25 | print("shell keys: " + str(list(traj['shell'].keys())))
26 |
27 | ax1 = plt.subplot(1, 2, 2)
28 |
29 | ax1.plot(traj.times, body_pos[:, 2], traj.times, plus_pos[:,2], traj.times, minus_pos[:,2])
30 |
31 | # Fire up SkellySim in "listener" mode
32 | listener = Listener(binary='skelly_sim_release')
33 |
34 | # All analysis requests are done via a "Request" object
35 | req = Request()
36 |
37 | # specify frame number to evaluate and evaluator (CPU, GPU, FMM)
38 | req.frame_no = 11
39 | req.evaluator = "GPU"
40 |
41 | # Request velocity field
42 | tmp = np.linspace(-shell_radius, shell_radius, 20)
43 | xm, ym, zm = np.meshgrid(tmp, tmp, tmp)
44 | xcube = np.array((xm.ravel(), ym.ravel(), zm.ravel())).T
45 | # Filter out points outside periphery and inside body
46 | relpoints = np.where((np.linalg.norm(xcube - body_pos[11,:], axis=1) > body_radius) &
47 | (np.linalg.norm(xcube, axis=1) < shell_radius))
48 | req.velocity_field.x = xcube[relpoints]
49 |
50 | # Make our request to SkellySim! Might take a second...
51 | res = listener.request(req)
52 |
53 | x = req.velocity_field.x
54 | v = res['velocity_field']
55 |
56 | ax2 = plt.subplot(1, 2, 2, projection="3d")
57 | ax2.quiver(x[:, 0], x[:, 1], x[:, 2], v[:, 0], v[:, 1], v[:, 2])
58 |
59 | plt.show()
60 |
--------------------------------------------------------------------------------
/examples/ellipsoid/gen_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 | import numpy as np
5 | from skelly_sim.skelly_config import ConfigEllipsoidal, Body, Fiber
6 |
7 | config_file = 'skelly_config.toml'
8 | if len(sys.argv) == 1:
9 | print(
10 | "Using default toml file for output: 'skelly_config.toml'. "
11 | "Provide an alternative filename argument to this script to use that instead."
12 | )
13 | elif len(sys.argv) != 2:
14 | print("Supply output config path (blahblah.toml) as sole argument")
15 | sys.exit()
16 | else:
17 | config_file = sys.argv[1]
18 |
19 | np.random.seed(100)
20 |
21 | n_fibers = 2000
22 |
23 | # create a config object and set the system parameters
24 | config = ConfigEllipsoidal()
25 | config.params.dt_write = 0.1
26 | config.params.dt_initial = 8E-3
27 | config.params.dt_max = 8E-3
28 |
29 | # generate a list of fibers. They don't have positions though, so we need to fill those in
30 | config.fibers = [
31 | Fiber(length=1.0,
32 | bending_rigidity=2.5E-3,
33 | parent_body=-1,
34 | force_scale=-0.05,
35 | minus_clamped=True,
36 | n_nodes=64) for i in range(n_fibers)
37 | ]
38 |
39 | config.periphery.n_nodes = 8000
40 |
41 | # move our fibers to the surface of the periphery and populate their position fields
42 | config.periphery.move_fibers_to_surface(config.fibers, ds_min=0.1)
43 |
44 | # output our config
45 | config.save(config_file)
46 |
47 | # uncomment the following to plot fibers on your ellipsoid
48 | # config.plot_fibers()
49 |
--------------------------------------------------------------------------------
/examples/listener_mode/README.md:
--------------------------------------------------------------------------------
1 | Point source with constant torque and force inside of a sphere. Measure streamlines, vortex
2 | lines, and velocity field in x-y plane.
3 |
4 | ```bash
5 | ./gen_config.py
6 | skelly_precompute
7 | skelly_sim
8 | ./listener_example.py
9 | ```
10 |
--------------------------------------------------------------------------------
/examples/listener_mode/gen_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 | import numpy as np
5 | from skelly_sim.skelly_config import ConfigSpherical, Point, Body
6 |
7 | config_file = 'skelly_config.toml'
8 | if len(sys.argv) == 1:
9 | print(
10 | "Using default toml file for output: 'skelly_config.toml'. "
11 | "Provide an alternative filename argument to this script to use that instead."
12 | )
13 | elif len(sys.argv) != 2:
14 | print("Supply output config path (blahblah.toml) as sole argument")
15 | sys.exit()
16 | else:
17 | config_file = sys.argv[1]
18 |
19 | np.random.seed(100)
20 |
21 | # create a config object and set the system parameters
22 | # config = ConfigSpherical()
23 | config = ConfigSpherical()
24 | config.params.eta = 1.0
25 | config.params.dt_initial = 1E-1
26 | config.params.dt_min = 1E-4
27 | config.params.dt_max = 1E-1
28 | config.params.dt_write = 1E-1
29 | config.params.t_final = 0.2
30 | config.params.gmres_tol = 1E-10
31 | config.params.seed = 130319
32 | config.params.pair_evaluator = "CPU"
33 |
34 | config.periphery.n_nodes = 2000
35 | config.periphery.radius = 3.0
36 |
37 | config.point_sources = [
38 | Point(position=[0.0, 0.0, 0.0], force=[0.0, 0.0, 1.0], torque=[0.0, 0.0, 10.0])
39 | ]
40 |
41 | # output our config
42 | config.save(config_file)
43 |
--------------------------------------------------------------------------------
/examples/listener_mode/listener_example.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import numpy as np
4 | from skelly_sim.reader import Listener, Request
5 | import matplotlib.pyplot as plt
6 | import matplotlib as mpl
7 | mpl.use('TKAgg')
8 |
9 | # Fire up SkellySim in "listener" mode
10 | listener = Listener()
11 |
12 | # All analysis requests are done via a "Request" object
13 | req = Request()
14 |
15 | # specify frame number to evaluate and evaluator (CPU, GPU, FMM)
16 | req.frame_no = 1
17 | req.evaluator = "GPU"
18 |
19 | # Request 3 streamlines from t=[-10, 10]
20 | req.streamlines.x0 = np.array([
21 | [0.25, 0.0, 0.0],
22 | [0.5, 0.0, 0.0],
23 | [1.0, 0.0, 0.0],
24 | ])
25 | req.streamlines.t_final = 10.0
26 |
27 | # Request single vortex line from t=[-10, 10]
28 | req.vortexlines.x0 = np.array([
29 | [0.0, 0.0, 2.0],
30 | ])
31 | req.vortexlines.t_final = 10.0
32 |
33 | # Request velocity field in z=0 plane
34 | tmp = np.linspace(-2, 2, 100)
35 | xm, ym, zm = np.meshgrid(tmp, tmp, 0.0)
36 | req.velocity_field.x = np.array((xm.ravel(), ym.ravel(), zm.ravel())).T
37 |
38 | # Make our request to SkellySim! Might take a second...
39 | res = listener.request(req)
40 |
41 | # Plot our streamlines
42 | ax1 = plt.subplot(1, 2, 1, projection='3d')
43 | for sl in res['streamlines']:
44 | x = sl['x'] # Points along streamline
45 | v = sl['val'] # Vector velocities at streamline evaluation points. Not used here, but available
46 | t = sl['time'] # time of evals. Not used here, but available
47 |
48 | ax1.plot3D(x[:,0], x[:,1], x[:,2])
49 |
50 |
51 | # Streamplot of x,y components of velocity field in z=0 plane
52 | ax2 = plt.subplot(1, 2, 2)
53 | xm, ym = xm.squeeze(axis=2), ym.squeeze(axis=2)
54 | U = res['velocity_field'][:, 0].reshape(*xm.shape)
55 | V = res['velocity_field'][:, 1].reshape(*ym.shape)
56 | ax2.streamplot(xm, ym, U, V)
57 |
58 | # IT'S GO TIME
59 | plt.show()
60 |
--------------------------------------------------------------------------------
/examples/oocyte/gen_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 | import toml
5 | import numpy as np
6 | from skelly_sim.skelly_config import ConfigRevolution, Body, Fiber
7 | from skelly_sim.shape_gallery import Envelope
8 |
9 | config_file = 'skelly_config.toml'
10 | if len(sys.argv) == 1:
11 | print(
12 | "Using default toml file for output: 'skelly_config.toml'. "
13 | "Provide an alternative filename argument to this script to use that instead."
14 | )
15 | elif len(sys.argv) != 2:
16 | print("Supply output config path (blahblah.toml) as sole argument")
17 | sys.exit()
18 | else:
19 | config_file = sys.argv[1]
20 |
21 | # This is just a script that creates a toml file using some utility functions I've written in in skelly_config
22 | numpy_seed = 100
23 | n_fibers = 3000
24 | np.random.seed(numpy_seed)
25 |
26 | # config is the object that will actually get turned into a toml file at the end
27 | config = ConfigRevolution()
28 |
29 | # system wide parameters
30 | config.params.dt_write = 0.1
31 | config.params.dt_initial = 1E-2
32 | config.params.dt_max = 1E-2
33 | config.params.periphery_interaction_flag = False
34 | config.params.seed = 350
35 | config.params.viscosity = 1
36 |
37 | # Create a list of identical base fibers to work with
38 | config.fibers = [
39 | Fiber(length=1.0,
40 | bending_rigidity=2.5E-3,
41 | force_scale=-0.05,
42 | minus_clamped=True,
43 | n_nodes=32) for i in range(n_fibers)
44 | ]
45 |
46 | # envelope is our surface of revolution. My algorithm tries to get the target nodes requested,
47 | # but will likely return an object with slightly more nodes. This is a required option
48 | config.periphery.envelope.n_nodes_target = 6000
49 | # lower/upper bound are required options. ideally your function should go to zero at the upper/lower bounds
50 | config.periphery.envelope.lower_bound = -3.75
51 | config.periphery.envelope.upper_bound = 3.75
52 | # required option. this is the function you're revolving around the 'x' axis. 'x' needs to be
53 | # the independent variable. Currently the function has to be a one-liner
54 | config.periphery.envelope.height = "0.5 * T * ((1 + 2*x/length)**p1) * ((1 - 2*x/length)**p2) * length"
55 | # All the parameters that go into our height function
56 | config.periphery.envelope.T = 0.72
57 | config.periphery.envelope.p1 = 0.4
58 | config.periphery.envelope.p2 = 0.2
59 | config.periphery.envelope.length = 7.5
60 |
61 | # minimum separation between fibers minus ends allowed on surface. distance is simple euclidean distance
62 | ds_min = 0.1
63 | config.periphery.move_fibers_to_surface(config.fibers, ds_min)
64 |
65 | # output our config
66 | config.save(config_file)
67 |
68 | # just uncomment this to show a quick visualization of fiber positions
69 | # config.plot_fibers()
70 |
--------------------------------------------------------------------------------
/examples/skelly_sim_slurm_sbatch.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 | #SBATCH --partition=scc
3 | #SBATCH --nodes=1
4 | #SBATCH --ntasks-per-node=8
5 | #SBATCH --exclusive
6 | #SBATCH --job-name=skelly_sim
7 | #SBATCH --output=skelly_sim.log
8 | #SBATCH --constraint=rome
9 |
10 | module purge
11 | module use ~rblackwell/modules
12 | module load gcc/11 openmpi intel-oneapi-mkl trilinos pvfmm stkfmm fftw flexiblas skelly_sim
13 | module list
14 | ldd $(which skelly_sim)
15 |
16 | export OMP_NUM_THREADS=$((${SLURM_CPUS_ON_NODE}/${SLURM_NTASKS_PER_NODE}))
17 | export OMP_PROC_BIND=spread
18 | export OMP_PLACES=threads
19 |
20 | srun --cpu-bind=ldoms skelly_sim --config-file=skelly_config.toml
21 |
--------------------------------------------------------------------------------
/examples/stokes_tests/body_const_force/calculate_velocity_error.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import numpy as np
4 | from skelly_sim.reader import TrajectoryReader
5 |
6 | traj = TrajectoryReader('skelly_config.toml')
7 |
8 | # need beginning/end positions to calculate average velocity
9 | traj.load_frame(0)
10 | z_initial = traj['bodies'][0]['position_'][2]
11 | traj.load_frame(len(traj) - 1)
12 | z_final = traj['bodies'][0]['position_'][2]
13 |
14 | dt = traj.times[-1] - traj.times[0]
15 |
16 | # We need the hydrodynamic radius, which is slightly different than the supplied 'attachment'
17 | # radius. let's just grab it from the precompute data.
18 | precompute_data = np.load(traj.config_data['bodies'][0]['precompute_file'])
19 | radius = np.linalg.norm(precompute_data["node_positions_ref"][0])
20 | eta = traj.config_data['params']['eta']
21 | force = traj.config_data['bodies'][0]['external_force'][2]
22 |
23 | v_theoretical = force / (6 * np.pi * eta * radius)
24 | v_measured = (z_final - z_initial) / dt
25 |
26 | print("Measured velocity: {}".format(v_measured))
27 | print("Theoretical velocity: {}".format(v_theoretical))
28 | print("Error |1 - v/v0|: {}".format(abs(1 - v_measured / v_theoretical)))
29 |
--------------------------------------------------------------------------------
/examples/stokes_tests/body_const_force/gen_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 | import numpy as np
5 | from skelly_sim.skelly_config import Config, Body
6 |
7 | config_file = 'skelly_config.toml'
8 | if len(sys.argv) == 1:
9 | print("Using default toml file for output: 'skelly_config.toml'. "
10 | "Provide an alternative filename argument to this script to use that instead.")
11 | elif len(sys.argv) != 2:
12 | print("Supply output config path (blahblah.toml) as sole argument")
13 | sys.exit()
14 | else:
15 | config_file = sys.argv[1]
16 |
17 | np.random.seed(100)
18 |
19 | # create a config object and set the system parameters
20 | config = Config()
21 | config.params.eta = 0.9
22 | config.params.dt_initial = 1E-1
23 | config.params.dt_min = 1E-4
24 | config.params.dt_max = 1E-1
25 | config.params.dt_write = 1E-1
26 | config.params.t_final = 10.0
27 | config.params.gmres_tol = 1E-10
28 | config.params.seed = 130319
29 | config.params.pair_evaluator = "CPU"
30 |
31 | config.bodies = [
32 | Body(n_nucleation_sites=0,
33 | position=[0.0, 0.0, 0.0],
34 | shape='sphere',
35 | radius=0.5,
36 | n_nodes=400,
37 | external_force=[0.0, 0.0, 1.5])
38 | ]
39 |
40 | config.save(config_file)
41 |
--------------------------------------------------------------------------------
/examples/stokes_tests/body_const_force_in_sphere/gen_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 | import numpy as np
5 | from skelly_sim.skelly_config import ConfigSpherical, Body, Fiber
6 |
7 | config_file = 'skelly_config.toml'
8 | if len(sys.argv) == 1:
9 | print(
10 | "Using default toml file for output: 'skelly_config.toml'. "
11 | "Provide an alternative filename argument to this script to use that instead."
12 | )
13 | elif len(sys.argv) != 2:
14 | print("Supply output config path (blahblah.toml) as sole argument")
15 | sys.exit()
16 | else:
17 | config_file = sys.argv[1]
18 |
19 | np.random.seed(100)
20 |
21 | # create a config object and set the system parameters
22 | config = ConfigSpherical()
23 | config.params.eta = 0.19884428157961156
24 | config.params.dt_initial = 1E-2
25 | config.params.dt_min = 1E-4
26 | config.params.dt_max = 1E-2
27 | config.params.dt_write = 1E-2
28 | config.params.t_final = 40.0
29 | config.params.gmres_tol = 1E-8
30 | config.params.seed = 130319
31 |
32 | config.bodies = [
33 | Body(n_nucleation_sites=0,
34 | position=[0.0, 0.0, 0.0],
35 | shape='sphere',
36 | radius=0.5,
37 | n_nodes=2000,
38 | external_force=[0.0, 0.0, 10.0])
39 | ]
40 |
41 | config.periphery.n_nodes = 6000
42 | config.periphery.radius = 4.0 / 1.04
43 |
44 | # output our config
45 | config.save(config_file)
46 |
47 | # uncomment the following to plot fibers on your ellipsoid
48 | # config.plot_fibers()
49 |
--------------------------------------------------------------------------------
/examples/stokes_tests/body_const_force_in_sphere/velocity_vs_z.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import numpy as np
4 | import matplotlib
5 |
6 | matplotlib.use('TKAgg')
7 | import matplotlib.pyplot as plt
8 |
9 | from skelly_sim.reader import TrajectoryReader
10 |
11 | traj = TrajectoryReader('skelly_config.toml')
12 | body_z = np.empty(shape=(len(traj))) # COM body position in time
13 |
14 | for i in range(len(traj)):
15 | traj.load_frame(i)
16 | body_z[i] = traj['bodies'][0]['position_'][2]
17 |
18 | v = np.diff(body_z) / np.diff(traj.times)
19 |
20 | # Compare to theoretical velocity of confined sphere
21 | body_precompute_data = np.load(traj.config_data['bodies'][0]['precompute_file'])
22 | shell_precompute_data = np.load(traj.config_data['periphery']['precompute_file'])
23 |
24 | r_body = np.linalg.norm(body_precompute_data["node_positions_ref"][0])
25 | r_shell = np.linalg.norm(shell_precompute_data["nodes"][0])
26 |
27 | lamb = r_body / r_shell
28 |
29 | f = traj.config_data['bodies'][0]['external_force'][-1]
30 | eta = traj.config_data['params']['eta']
31 | gamma_theory = 6 * np.pi * r_body * 4 * eta * (1-lamb**5) / (4-9*lamb + 10*lamb**3 - 9*lamb**5 + 4*lamb**6)
32 | v_theory = f / gamma_theory
33 | gamma_compute = f / v[0]
34 |
35 | print(r_body, r_shell, lamb)
36 | print(v[0], v_theory, np.abs(1.0 - v[0]/v_theory))
37 | print(gamma_compute, gamma_theory, np.abs(1.0 - gamma_compute/gamma_theory))
38 |
39 | def mu_bar(z, a=r_body, R=r_shell):
40 | return 1 - 9 * a * (R*R / (R*R - z*z)) / 4 / R
41 |
42 | gamma_vs_z = 6 * np.pi * eta * r_body / np.array(list(map(mu_bar, body_z[1:])))
43 |
44 | plt.plot(body_z[1:], v)
45 | plt.plot(body_z[1:], f / gamma_vs_z)
46 | plt.xlabel('Body z')
47 | plt.ylabel('Body velocity')
48 | plt.legend(['SkellySim', '1 - 9 * a (R^2 / (R^2 - r^2)) / 4 / R'])
49 | plt.title(f'Velocity of body in periphery a={r_body}, R={r_shell}')
50 | #plt.ylim([0, 1.5])
51 | plt.show()
52 |
--------------------------------------------------------------------------------
/examples/stokes_tests/clamped_buckling/README.md:
--------------------------------------------------------------------------------
1 | Test for a single clamped fiber with a constant force per unit length toward the clamping
2 | point. Values of `sigma < 76` should have decaying fiber oscillations, and values of `sigma >
3 | 76` should have growing oscillations. The python script, when run in either subdirectory, will
4 | plot the `x` position of the unclamped end of the fiber.
5 |
6 | Credit: Brato Chakrabarti for the [derivation](hopf-bifurcation.pdf)
7 |
8 | # Sigma = 72
9 | 
10 |
11 | # Sigma = 80
12 | 
13 |
--------------------------------------------------------------------------------
/examples/stokes_tests/clamped_buckling/hopf-bifurcation.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/examples/stokes_tests/clamped_buckling/hopf-bifurcation.pdf
--------------------------------------------------------------------------------
/examples/stokes_tests/clamped_buckling/plot_deflection.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from skelly_sim.reader import TrajectoryReader
4 |
5 | import matplotlib
6 | matplotlib.use('TKAgg')
7 | import matplotlib.pyplot as plt
8 |
9 | def get_deflection(path: str):
10 | traj = TrajectoryReader(path)
11 |
12 | x = []
13 | for i in range(len(traj)):
14 | traj.load_frame(i)
15 | x.append(traj['fibers'][0]['x_'][-1, 0])
16 |
17 | return traj.times, x
18 |
19 | t72, x72 = get_deflection('sigma72/skelly_config.toml')
20 | t80, x80 = get_deflection('sigma80/skelly_config.toml')
21 |
22 | plt.plot(t72, x72, t80, x80)
23 | plt.legend(['\u03c3 = 72', '\u03c3 = 80'])
24 | plt.xlabel('Time (s)')
25 | plt.ylabel('Deflection (micrometers)')
26 | plt.show()
27 |
--------------------------------------------------------------------------------
/examples/stokes_tests/clamped_buckling/sigma72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/examples/stokes_tests/clamped_buckling/sigma72.png
--------------------------------------------------------------------------------
/examples/stokes_tests/clamped_buckling/sigma72/gen_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import numpy as np
4 | from skelly_sim.skelly_config import Config, Fiber, Point
5 |
6 | np.random.seed(100)
7 |
8 | # create a config object and set the system parameters
9 | config = Config()
10 | config.params.eta = 1.0
11 | config.params.dt_initial = 0.02
12 | config.params.dt_max = 0.02
13 | config.params.dt_write = 0.1
14 | config.params.t_final = 200.0
15 | config.params.pair_evaluator = "CPU"
16 |
17 | sigma = 72.
18 |
19 | length = 1.0
20 | bending_rigidity = 0.0025
21 | force_scale = -sigma * bending_rigidity / length**3
22 | n_nodes = 32
23 |
24 | config.fibers = [
25 | Fiber(
26 | force_scale=force_scale,
27 | length=length,
28 | n_nodes=n_nodes,
29 | bending_rigidity=bending_rigidity,
30 | minus_clamped=True,
31 | )
32 | ]
33 |
34 | # give a small kick to start from
35 | config.point_sources = [
36 | Point(
37 | position=[0.0, 0.0, 10 * length],
38 | force=[10.0, 0.0, 0.0],
39 | time_to_live=1.0,
40 | )
41 | ]
42 |
43 | # orient in z
44 | config.fibers[0].x = np.linspace([0, 0, 0], [0, 0, length], n_nodes).ravel().tolist()
45 |
46 | # output our config
47 | config.save()
48 |
--------------------------------------------------------------------------------
/examples/stokes_tests/clamped_buckling/sigma80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/examples/stokes_tests/clamped_buckling/sigma80.png
--------------------------------------------------------------------------------
/examples/stokes_tests/clamped_buckling/sigma80/gen_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import numpy as np
4 | from skelly_sim.skelly_config import Config, Fiber, Point
5 |
6 | # create a config object and set the system parameters
7 | config = Config()
8 | config.params.eta = 1.0
9 | config.params.dt_initial = 0.02
10 | config.params.dt_max = 0.02
11 | config.params.dt_write = 0.1
12 | config.params.t_final = 200.0
13 | config.params.pair_evaluator = "CPU"
14 |
15 | sigma = 80.
16 |
17 | length = 1.0
18 | bending_rigidity = 0.0025
19 | force_scale = -sigma * bending_rigidity / length**3
20 | n_nodes = 32
21 |
22 | config.fibers = [
23 | Fiber(
24 | force_scale=force_scale,
25 | length=length,
26 | n_nodes=n_nodes,
27 | bending_rigidity=bending_rigidity,
28 | minus_clamped=True,
29 | )
30 | ]
31 |
32 | # give a small kick to start from
33 | config.point_sources = [
34 | Point(
35 | position=[0.0, 0.0, 10 * length],
36 | force=[10.0, 0.0, 0.0],
37 | time_to_live=1.0,
38 | )
39 | ]
40 |
41 | # orient in z
42 | config.fibers[0].x = np.linspace([0, 0, 0], [0, 0, length], n_nodes).ravel().tolist()
43 |
44 | # output our config
45 | config.save()
46 |
--------------------------------------------------------------------------------
/examples/stokes_tests/fiber_const_force/calc_velocity.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from skelly_sim.reader import TrajectoryReader
4 | import numpy as np
5 |
6 | traj = TrajectoryReader('skelly_config.toml')
7 |
8 | traj.load_frame(0)
9 | x0 = traj['fibers'][0]['x_'][0, :]
10 | traj.load_frame(-1)
11 | xf = traj['fibers'][0]['x_'][0, :]
12 |
13 | dt = traj.times[-1] - traj.times[0]
14 | v = (xf - x0) / dt
15 |
16 | fib = traj.config_data['fibers'][0]
17 | length = fib['length']
18 | radius = fib['radius']
19 |
20 | epsilon = radius / length
21 |
22 | gamma = fib["force_scale"] * length / v[-1]
23 | gamma_theory = -4 * np.pi * length * traj.config_data['params']['eta'] / (np.log(np.exp(1) * epsilon**2))
24 |
25 | print("theoretical drag: {}".format(gamma_theory))
26 | print("measured drag: {}".format(gamma))
27 | print("relative error: {}".format(abs(1 - gamma/gamma_theory)))
28 |
29 |
--------------------------------------------------------------------------------
/examples/stokes_tests/fiber_const_force/gen_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 | import numpy as np
5 | from skelly_sim.skelly_config import Config, Fiber
6 |
7 | config_file = 'skelly_config.toml'
8 | if len(sys.argv) == 1:
9 | print("Using default toml file for output: 'skelly_config.toml'. "
10 | "Provide an alternative filename argument to this script to use that instead.")
11 | elif len(sys.argv) != 2:
12 | print("Supply output config path (blahblah.toml) as sole argument")
13 | sys.exit()
14 | else:
15 | config_file = sys.argv[1]
16 |
17 | np.random.seed(100)
18 |
19 | # create a config object and set the system parameters
20 | config = Config()
21 | config.params.eta = 0.7
22 | config.params.dt_initial = 1E-4
23 | config.params.dt_min = 1E-4
24 | config.params.dt_max = 1E-4
25 | config.params.dt_write = 1E-3
26 | config.params.t_final = 1E-2
27 | config.params.gmres_tol = 1E-10
28 | config.params.seed = 130319
29 | config.params.pair_evaluator = "CPU"
30 |
31 | length = 0.75
32 | config.fibers = [Fiber(
33 | force_scale=0.31,
34 | length=length,
35 | n_nodes=8,
36 | bending_rigidity=0.0025
37 | )]
38 | config.fibers[0].fill_node_positions(np.array([0.0, 0.0, 0.0]), np.array([0.0, 0.0, 1.0]))
39 |
40 | # output our config
41 | config.save(config_file)
42 |
--------------------------------------------------------------------------------
/examples/stokes_tests/fiber_const_force_in_sphere/README.md:
--------------------------------------------------------------------------------
1 | Not the most useful test, but interesting. Drag/velocity of a fiber with constant force/length
2 | inside of a spherical periphery should be independent of position, which SkellySim supports.
3 |
4 | ```
5 | % ./calc_velocity.py
6 | theoretical drag: 0.9177395848298898
7 | measured drag: 0.9177395848290841
8 | relative error: 8.778533455711113e-13
9 | ```
10 |
--------------------------------------------------------------------------------
/examples/stokes_tests/fiber_const_force_in_sphere/calc_velocity.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from skelly_sim.reader import TrajectoryReader
4 | import numpy as np
5 |
6 | traj = TrajectoryReader('skelly_config.toml')
7 |
8 | traj.load_frame(0)
9 | x0 = traj['fibers'][0]['x_'][0, :]
10 | traj.load_frame(-1)
11 | xf = traj['fibers'][0]['x_'][0, :]
12 |
13 | dt = traj.times[-1] - traj.times[0]
14 | v = (xf - x0) / dt
15 |
16 | fib = traj.config_data['fibers'][0]
17 | length = fib['length']
18 | radius = fib['radius']
19 |
20 | epsilon = radius / length
21 |
22 | gamma = fib["force_scale"] * length / v[-1]
23 | gamma_theory = -4 * np.pi * length * traj.config_data['params']['eta'] / (np.log(np.exp(1) * epsilon**2))
24 |
25 | print("theoretical drag: {}".format(gamma_theory))
26 | print("measured drag: {}".format(gamma))
27 | print("relative error: {}".format(abs(1 - gamma/gamma_theory)))
28 |
29 |
--------------------------------------------------------------------------------
/examples/stokes_tests/fiber_const_force_in_sphere/gen_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 | import numpy as np
5 | from skelly_sim.skelly_config import ConfigSpherical, Fiber
6 |
7 | config_file = 'skelly_config.toml'
8 | if len(sys.argv) == 1:
9 | print("Using default toml file for output: 'skelly_config.toml'. "
10 | "Provide an alternative filename argument to this script to use that instead.")
11 | elif len(sys.argv) != 2:
12 | print("Supply output config path (blahblah.toml) as sole argument")
13 | sys.exit()
14 | else:
15 | config_file = sys.argv[1]
16 |
17 | np.random.seed(100)
18 |
19 | # create a config object and set the system parameters
20 | config = ConfigSpherical()
21 | config.params.eta = 1.0
22 | config.params.dt_initial = 1E-2
23 | config.params.dt_write = 1E-1
24 | config.params.t_final = 1.0
25 | config.params.gmres_tol = 1E-10
26 | config.params.seed = 130319
27 | config.params.adaptive_timestep_flag = False
28 |
29 | length = 0.75
30 | config.fibers = [Fiber(
31 | force_scale=0.31,
32 | length=length,
33 | n_nodes=8,
34 | bending_rigidity=0.0025
35 | )]
36 | config.fibers[0].fill_node_positions(np.array([1.0, 0.0, 0.0]), np.array([0.0, 0.0, 1.0]))
37 |
38 | config.periphery.n_nodes = 4000
39 | config.periphery.radius = 4.0
40 |
41 | # output our config
42 | config.save(config_file)
43 |
--------------------------------------------------------------------------------
/examples/stokes_tests/ps_const_force_in_sphere/README.md:
--------------------------------------------------------------------------------
1 | Point source with constant force inside of a sphere. Measure velocity field and compare
2 | against known codes.
3 |
--------------------------------------------------------------------------------
/examples/stokes_tests/ps_const_force_in_sphere/analysis_example.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import numpy as np
4 | from skelly_sim.reader import Listener, Request
5 | import matplotlib as mpl
6 | import matplotlib.pyplot as plt
7 | mpl.use('TKAgg')
8 |
9 | # Fire up SkellySim in "listener" mode
10 | listener = Listener(binary='skelly_sim_release')
11 |
12 | # All analysis requests are done via a "Request" object
13 | req = Request()
14 |
15 | # specify frame number to evaluate and evaluator (CPU, GPU, FMM)
16 | req.frame_no = 0
17 | req.evaluator = "CPU"
18 |
19 | # Request velocity field in z=0 plane
20 | tmp = np.linspace(-2, 2, 1000)
21 | xm, ym, zm = np.meshgrid(tmp, tmp, 0.0)
22 | req.velocity_field.x = np.array((xm.ravel(), ym.ravel(), zm.ravel())).T
23 |
24 | # Make our request to SkellySim! Might take a second...
25 | res = listener.request(req)
26 |
27 | # Streamplot of x,y components of velocity field in z=0 plane
28 | ax1 = plt.subplot(1, 1, 1)
29 | xm, ym = xm.squeeze(axis=2), ym.squeeze(axis=2)
30 | U = res['velocity_field'][:, 0].reshape(*xm.shape)
31 | V = res['velocity_field'][:, 1].reshape(*ym.shape)
32 | ax1.streamplot(xm, ym, U, V)
33 |
34 | # IT'S GO TIME
35 | plt.show()
36 |
--------------------------------------------------------------------------------
/examples/stokes_tests/ps_const_force_in_sphere/gen_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 | import numpy as np
5 | from skelly_sim.skelly_config import ConfigSpherical, Config, Point, Body
6 |
7 | config_file = 'skelly_config.toml'
8 |
9 | np.random.seed(100)
10 |
11 | # create a config object and set the system parameters
12 | # config = ConfigSpherical()
13 | config = ConfigSpherical()
14 | config.params.eta = 0.7
15 | config.params.dt_initial = 1E-1
16 | config.params.dt_min = 1E-4
17 | config.params.dt_max = 1E-1
18 | config.params.dt_write = 1E-1
19 | config.params.t_final = 0.2
20 | config.params.gmres_tol = 1E-10
21 | config.params.seed = 130319
22 |
23 | config.periphery.n_nodes = 6000
24 | config.periphery.radius = np.pi / 3 / 1.04
25 |
26 | config.point_sources = [
27 | Point(position=((np.random.uniform(size=3) - 0.5)*0.6).tolist(),
28 | force=(np.random.uniform(size=3) - 0.5).tolist(),
29 | )
30 | ]
31 |
32 | # output our config
33 | config.save(config_file)
34 |
--------------------------------------------------------------------------------
/examples/stokes_tests/ps_const_torque_in_sphere/README.md:
--------------------------------------------------------------------------------
1 | Point source with constant torque inside of a sphere. Measure velocity field and compare
2 | against known codes.
3 |
--------------------------------------------------------------------------------
/examples/stokes_tests/ps_const_torque_in_sphere/analysis_example.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import numpy as np
4 | from skelly_sim.reader import Listener, Request
5 | import matplotlib as mpl
6 | import matplotlib.pyplot as plt
7 |
8 | # Fire up SkellySim in "listener" mode
9 | listener = Listener(binary='skelly_sim_release')
10 |
11 | # All analysis requests are done via a "Request" object
12 | req = Request()
13 |
14 | # specify frame number to evaluate and evaluator (CPU, GPU, FMM)
15 | req.frame_no = 0
16 | req.evaluator = "CPU"
17 |
18 | # Request velocity field in z=0 plane
19 | tmp = np.linspace(-2, 2, 1000)
20 | xm, ym, zm = np.meshgrid(tmp, tmp, 0.0)
21 | req.velocity_field.x = np.array((xm.ravel(), ym.ravel(), zm.ravel())).T
22 |
23 | # Make our request to SkellySim! Might take a second...
24 | res = listener.request(req)
25 |
26 | # Streamplot of x,y components of velocity field in z=0 plane
27 | ax1 = plt.subplot(1, 1, 1)
28 | xm, ym = xm.squeeze(axis=2), ym.squeeze(axis=2)
29 | U = res['velocity_field'][:, 0].reshape(*xm.shape)
30 | V = res['velocity_field'][:, 1].reshape(*ym.shape)
31 | ax1.streamplot(xm, ym, U, V)
32 |
33 | # IT'S GO TIME
34 | plt.show()
35 |
--------------------------------------------------------------------------------
/examples/stokes_tests/ps_const_torque_in_sphere/gen_config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import numpy as np
4 | from skelly_sim.skelly_config import ConfigSpherical, Config, Point, Body
5 |
6 | config_file = 'skelly_config.toml'
7 | np.random.seed(100)
8 |
9 | # create a config object and set the system parameters
10 | # config = ConfigSpherical()
11 | config = ConfigSpherical()
12 | config.params.eta = 0.7
13 | config.params.dt_initial = 1E-1
14 | config.params.dt_min = 1E-4
15 | config.params.dt_max = 1E-1
16 | config.params.dt_write = 1E-1
17 | config.params.t_final = 0.2
18 | config.params.gmres_tol = 1E-10
19 | config.params.seed = 130319
20 |
21 | config.periphery.n_nodes = 4000
22 | config.periphery.radius = np.pi / 3 / 1.04
23 |
24 | config.point_sources = [
25 | Point(position=((np.random.uniform(size=3) - 0.5)*0.6).tolist(),
26 | torque=(np.random.uniform(size=3) - 0.5).tolist(),
27 | )
28 | ]
29 |
30 | # output our config
31 | config.save(config_file)
32 |
--------------------------------------------------------------------------------
/include/background_source.hpp:
--------------------------------------------------------------------------------
1 | #ifndef BACKGROUND_SOURCE_HPP
2 | #define BACKGROUND_SOURCE_HPP
3 |
4 | #include
5 |
6 | class BackgroundSource {
7 | public:
8 | BackgroundSource() = default;
9 | BackgroundSource(const toml::value &background_table);
10 | Eigen::MatrixXd flow(CMatrixRef &r_trg, double eta);
11 | bool is_active() { return uniform_.norm() + scale_factor_.norm(); }
12 |
13 | private:
14 | Eigen::Vector3i components_ = {0, 1, 2};
15 | Eigen::Vector3d scale_factor_ = {0.0, 0.0, 0.0};
16 | Eigen::Vector3d uniform_ = {0.0, 0.0, 0.0};
17 | };
18 |
19 | #endif
20 |
--------------------------------------------------------------------------------
/include/body.hpp:
--------------------------------------------------------------------------------
1 | #ifndef BODY_HPP
2 | #define BODY_HPP
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | class Periphery;
11 | class SphericalBody;
12 | class DeformableBody;
13 | class EllipsoidalBody;
14 | class FiberContainerFiniteDifference;
15 |
16 | /// Class for "small" bodies such as MTOCs
17 | class Body {
18 | public:
19 | enum EXTFORCE { Linear, Oscillatory }; ///< Type of external force [Linear, Oscillatory]
20 | static const std::string EXTFORCE_name[]; ///< String name of external force
21 | int n_nodes_; ///< Number of nodes representing the body surface
22 |
23 | Eigen::VectorXd RHS_; ///< Current 'right-hand-side' for matrix formulation of solver
24 | Eigen::MatrixXd node_positions_; ///< [ 3 x n_nodes ] node positions in lab frame
25 | Eigen::MatrixXd node_positions_ref_; ///< [ 3 x n_nodes ] node positions in reference 'body' frame
26 | Eigen::MatrixXd node_normals_; ///< [ 3 x n_nodes ] node normals in lab frame
27 | Eigen::MatrixXd node_normals_ref_; ///< [ 3 x n_nodes ] node normals in reference 'body' frame
28 | Eigen::VectorXd node_weights_; ///< [ n_nodes ] far field quadrature weights for nodes
29 | Eigen::VectorXd solution_vec_; ///< [ 3 * n_nodes + ] strength of interaction on nodes
30 |
31 | /// [ 3 x n_nucleation_sites ] nucleation site positions in reference 'body' frame
32 | Eigen::MatrixXd nucleation_sites_ref_;
33 | /// [ 3 x n_nucleation_sites ] nucleation site positions in lab frame
34 | Eigen::MatrixXd nucleation_sites_;
35 |
36 | Body(const toml::value &body_table, const Params ¶ms);
37 | Body() = default; ///< default constructor...
38 |
39 | virtual void update_RHS(CMatrixRef &v_on_body) = 0;
40 | virtual void update_cache_variables(double eta) = 0;
41 | virtual void update_preconditioner(double eta) = 0;
42 | virtual void load_precompute_data(const std::string &input_file) = 0;
43 | virtual void step(double dt, CVectorRef &body_solution) = 0;
44 |
45 | virtual int get_solution_size() const = 0;
46 | virtual Eigen::Vector3d get_position() const = 0;
47 | virtual Eigen::VectorXd matvec(CMatrixRef &v_bodies, CVectorRef &body_solution) const = 0;
48 | virtual Eigen::VectorXd apply_preconditioner(CVectorRef &x) const = 0;
49 |
50 | /// @brief Make a copy of this instance
51 | virtual std::shared_ptr clone() const = 0;
52 |
53 | /// @brief dummy method to be overriden by derived classes
54 | virtual bool check_collision(const Periphery &periphery, double threshold) const = 0;
55 |
56 | /// @brief dummy method to be overriden by derived classes
57 | virtual bool check_collision(const Body &body, double threshold) const = 0;
58 |
59 | /// @brief dummy method to be overriden by derived classes
60 | virtual bool check_collision(const SphericalBody &body, double threshold) const = 0;
61 |
62 | /// @brief dummy method to be overriden by derived classes
63 | virtual bool check_collision(const DeformableBody &body, double threshold) const = 0;
64 |
65 | /// @brief dummy method to be overriden by derived classes
66 | virtual bool check_collision(const EllipsoidalBody &body, double threshold) const = 0;
67 |
68 | /// For structures with fixed size Eigen::Vector types, this ensures alignment if the
69 | /// structure is allocated via `new`
70 | EIGEN_MAKE_ALIGNED_OPERATOR_NEW
71 | };
72 |
73 | #endif
74 |
--------------------------------------------------------------------------------
/include/body_deformable.hpp:
--------------------------------------------------------------------------------
1 | #ifndef BODY_DEFORMABLE_HPP
2 | #define BODY_DEFORMABLE_HPP
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | class Periphery;
12 | class SphericalBody;
13 | class EllipsoidalBody;
14 | class FiberContainerFiniteDifference;
15 |
16 | /// @brief Spherical Body...
17 | class DeformableBody : public Body {
18 | public:
19 | /// @brief Construct deformable body. @see Body
20 | /// @param[in] body_table Parsed TOML body table
21 | /// @param[in] params Initialized Params object
22 | DeformableBody(const toml::value &body_table, const Params ¶ms) : Body(body_table, params){};
23 | DeformableBody() = default;
24 |
25 | /// Duplicate SphericalBody object
26 | std::shared_ptr clone() const override { return std::make_shared(*this); };
27 |
28 | void min_copy(const std::shared_ptr &other);
29 |
30 | void update_RHS(CMatrixRef &v_on_body) override;
31 | void update_cache_variables(double eta) override;
32 | void update_preconditioner(double eta) override;
33 | void load_precompute_data(const std::string &input_file) override;
34 | void step(double dt, CVectorRef &body_solution) override;
35 | int get_solution_size() const override { return n_nodes_ * 4; };
36 | Eigen::VectorXd matvec(CMatrixRef &v_bodies, CVectorRef &body_solution) const override;
37 | Eigen::VectorXd apply_preconditioner(CVectorRef &x) const override;
38 | Eigen::Vector3d get_position() const override;
39 |
40 | bool check_collision(const Periphery &periphery, double threshold) const override;
41 | bool check_collision(const Body &body, double threshold) const override;
42 | bool check_collision(const SphericalBody &body, double threshold) const override;
43 | bool check_collision(const DeformableBody &body, double threshold) const override;
44 | bool check_collision(const EllipsoidalBody &body, double threshold) const override;
45 |
46 | MSGPACK_DEFINE_MAP(node_positions_, node_normals_, solution_vec_);
47 | };
48 |
49 | #endif
50 |
--------------------------------------------------------------------------------
/include/eigen_matrix_plugin.h:
--------------------------------------------------------------------------------
1 | #ifndef EIGEN_MATRIX_PLUGIN_H
2 | #define EIGEN_MATRIX_PLUGIN_H
3 |
4 | inline void msgpack_unpack(msgpack::object o) {
5 | if(o.type != msgpack::type::ARRAY) { throw msgpack::type_error(); }
6 |
7 | msgpack::object * p = o.via.array.ptr;
8 |
9 | std::string type;
10 | *p >> type;
11 | if (type != "__eigen__") { throw msgpack::type_error(); }
12 |
13 | size_t rows;
14 | size_t cols;
15 |
16 | ++p;
17 | *p >> rows;
18 | ++p;
19 | *p >> cols;
20 | this->resize(rows, cols);
21 |
22 | for (int i = 0; i < this->cols(); ++i) {
23 | for (int j = 0; j < this->rows(); ++j) {
24 | ++p;
25 | *p >> this->operator()(j, i);
26 | }
27 | }
28 | }
29 |
30 | template
31 | inline void msgpack_pack(Packer& pk) const {
32 | pk.pack_array(3 + this->rows()*this->cols());
33 | pk.pack(std::string("__eigen__"));
34 | pk.pack(this->rows());
35 | pk.pack(this->cols());
36 |
37 | for (int i = 0; i < this->cols(); ++i) {
38 | for (int j = 0; j < this->rows(); ++j) {
39 | pk.pack(this->operator()(j, i));
40 | }
41 | }
42 | }
43 |
44 | template
45 | inline void msgpack_object(MSGPACK_OBJECT* o, msgpack::zone* z) const { }
46 |
47 | #endif
48 |
--------------------------------------------------------------------------------
/include/eigen_quaternion_plugin.h:
--------------------------------------------------------------------------------
1 | #ifndef EIGEN_QUATERNION_PLUGIN_H
2 | #define EIGEN_QUATERNION_PLUGIN_H
3 |
4 | inline void msgpack_unpack(msgpack::object o) {
5 | if (o.type != msgpack::type::ARRAY) {
6 | throw msgpack::type_error();
7 | }
8 |
9 | msgpack::object *p = o.via.array.ptr;
10 |
11 | std::string type;
12 | *p >> type;
13 | if (type != "__quat__") {
14 | throw msgpack::type_error();
15 | }
16 |
17 | ++p;
18 | *p >> this->w();
19 | ++p;
20 | *p >> this->x();
21 | ++p;
22 | *p >> this->y();
23 | ++p;
24 | *p >> this->z();
25 | }
26 |
27 | template
28 | inline void msgpack_pack(Packer &pk) const {
29 | pk.pack_array(5);
30 | pk.pack(std::string("__quat__"));
31 |
32 | pk.pack(this->w());
33 | pk.pack(this->x());
34 | pk.pack(this->y());
35 | pk.pack(this->z());
36 | }
37 |
38 | template
39 | inline void msgpack_object(MSGPACK_OBJECT *o, msgpack::zone *z) const {}
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/include/io_maps.hpp:
--------------------------------------------------------------------------------
1 | #ifndef IO_MAPS_HPP
2 | #define IO_MAPS_HPP
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include
12 |
13 | /// @brief Structure for importing frame of trajectory into the simulation
14 | ///
15 | /// We can't use output_map_t here, but rather a similar struct which uses copies of the member
16 | /// variables (rather than references) which are then used to update the System variables.
17 | typedef struct input_map_t {
18 | double time; ///< System::properties
19 | double dt; ///< System::properties
20 | std::unique_ptr fibers; ///< System::fc_
21 | BodyContainer bodies; ///< System::bc_
22 | Periphery shell; ///< System::shell_
23 | std::vector> rng_state; ///< string representation of split/unsplit state in RNG
24 | MSGPACK_DEFINE_MAP(time, dt, rng_state, fibers, bodies, shell); ///< Helper routine to specify serialization
25 | } input_map_t;
26 |
27 | /// @brief Structure for trajectory output via msgpack
28 | ///
29 | /// This can be extended easily, so long as you update the corresponding input_map_t and potentially the System::write()
30 | /// function if you can't use a reference in the output_map for some reason.
31 | typedef struct output_map_t {
32 | double &time; ///< System::properties
33 | double &dt; ///< System::properties
34 | std::unique_ptr &fibers; ///< System::fc_
35 | BodyContainer &bodies; ///< System::bc_
36 | Periphery &shell; ///< System::shell_
37 | std::vector> rng_state; ///< string representation of split/unsplit state in RNG
38 | MSGPACK_DEFINE_MAP(time, dt, rng_state, fibers, bodies, shell); ///< Helper routine to specify serialization
39 | } output_map_t;
40 |
41 | /// @brief Structure for trajectory header information via msgpack
42 | ///
43 | /// Contains all of the header information for the simulation
44 | typedef struct header_map_t {
45 | // Make sure that the trajectory version always comes first!!!!!
46 | int trajversion; ///< SKELLYSIM_TRAJECTORY_VERSION
47 | int number_mpi_ranks; ///< System::rank_
48 | int fiber_type; ///< System::fc_->fiber_type_
49 | std::string skellysim_version; ///< SKELLYSIM_VERSION
50 | std::string skellysim_commit; ///< SKELLYSIM_COMMIT
51 | std::string simdate; ///< Date of simulation from chrono and ctime
52 | std::string hostname; ///< Hostnames of the simulation
53 | MSGPACK_DEFINE_MAP(trajversion, number_mpi_ranks, fiber_type, skellysim_version, skellysim_commit, simdate,
54 | hostname);
55 | } header_map_t;
56 |
57 | #endif
58 |
--------------------------------------------------------------------------------
/include/listener.hpp:
--------------------------------------------------------------------------------
1 | #ifndef LISTENER_HPP
2 | #define LISTENER_HPP
3 |
4 | namespace listener {
5 | void run();
6 | }
7 |
8 | #endif
9 |
--------------------------------------------------------------------------------
/include/params.hpp:
--------------------------------------------------------------------------------
1 | #ifndef PARAMS_HPP
2 | #define PARAMS_HPP
3 |
4 | #include
5 |
6 | /// Class containing input parameters for the simulated system
7 | class Params {
8 | public:
9 | double eta;
10 | double dt_initial;
11 | double beta_up;
12 | double beta_down;
13 | double gmres_tol;
14 | double t_final;
15 | double fiber_error_tol;
16 | double dt_min;
17 | double dt_max;
18 | double dt_write;
19 | double implicit_motor_activation_delay;
20 | bool periphery_interaction_flag;
21 | bool adaptive_timestep_flag;
22 | std::string pair_evaluator;
23 | std::string fiber_type;
24 |
25 | periphery_binding_t periphery_binding{
26 | .polar_angle_start = 0.0,
27 | .polar_angle_end = M_PI,
28 | .threshold = 0.75,
29 | };
30 |
31 | struct {
32 | int n_nodes = 0;
33 | double v_growth;
34 | double f_catastrophe;
35 | double v_grow_collision_scale;
36 | double f_catastrophe_collision_scale;
37 | double nucleation_rate;
38 | double min_length;
39 | double radius;
40 | double bending_rigidity;
41 | double min_separation;
42 | } dynamic_instability;
43 | unsigned long seed;
44 |
45 | fiber_periphery_interaction_t fiber_periphery_interaction{
46 | .f_0 = 20.0,
47 | .l_0 = 0.05,
48 | };
49 |
50 | struct {
51 | int body_stresslet_multipole_order = 8;
52 | int body_stresslet_max_points = 2000;
53 | int body_oseen_multipole_order = 8;
54 | int body_oseen_max_points = 2000;
55 | int fiber_stokeslet_multipole_order = 8;
56 | int fiber_stokeslet_max_points = 2000;
57 | int periphery_stresslet_multipole_order = 8;
58 | int periphery_stresslet_max_points = 2000;
59 | } stkfmm;
60 |
61 | std::string shell_precompute_file;
62 |
63 | Params() = default;
64 | Params(toml::value ¶m_table);
65 |
66 | void print();
67 | };
68 |
69 | #endif
70 |
--------------------------------------------------------------------------------
/include/parse_util.hpp:
--------------------------------------------------------------------------------
1 | #ifndef PARSE_UTIL_HPP
2 | #define PARSE_UTIL_HPP
3 |
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | namespace parse_util {
10 |
11 | template
12 | inline T convert_array(const toml::array &src) {
13 | if constexpr (std::is_same_v) {
14 | double tmp[4];
15 | for (size_t i = 0; i < src.size(); ++i)
16 | tmp[i] = src.at(i).as_floating();
17 |
18 | return Eigen::Quaterniond(tmp);
19 | }
20 | else if constexpr (std::is_same_v) {
21 | T trg;
22 | for (size_t i = 0; i < 3; ++i)
23 | trg[i] = src.at(i).as_integer();
24 |
25 | return trg;
26 | }
27 | else {
28 | T trg(src.size());
29 | for (size_t i = 0; i < src.size(); ++i)
30 | trg[i] = src.at(i).as_floating();
31 |
32 | return trg;
33 | }
34 | }
35 |
36 | } // namespace parse_util
37 | #endif
38 |
--------------------------------------------------------------------------------
/include/point_source.hpp:
--------------------------------------------------------------------------------
1 | #ifndef POINT_SOURCE_HPP
2 | #define POINT_SOURCE_HPP
3 |
4 | #include
5 | #include
6 |
7 | class PointSource {
8 | public:
9 | double time_to_live_ = 0.0;
10 | Eigen::Vector3d position_ = {0.0, 0.0, 0.0};
11 | Eigen::Vector3d force_ = {0.0, 0.0, 0.0};
12 | Eigen::Vector3d torque_ = {0.0, 0.0, 0.0};
13 |
14 | PointSource(const toml::value &point_table);
15 | };
16 |
17 | class PointSourceContainer {
18 | public:
19 | PointSourceContainer() = default;
20 | PointSourceContainer(const toml::array &point_tables);
21 |
22 | Eigen::MatrixXd flow(CMatrixRef &r_trg, double eta, double time);
23 | std::vector points;
24 | };
25 |
26 | #endif
27 |
--------------------------------------------------------------------------------
/include/rng.hpp:
--------------------------------------------------------------------------------
1 | #ifndef RNG_HPP
2 | #define RNG_HPP
3 |
4 | #include
5 |
6 | namespace RNG {
7 | void init(unsigned long seed);
8 | void init(std::pair);
9 |
10 | std::pair dump_state();
11 |
12 | double uniform(double low = 0.0, double high = 1.0);
13 | int uniform_int(int low, int high);
14 | double normal(double mu = 0.0, double sigma = 1.0);
15 | int poisson_int(double mu);
16 |
17 | double uniform_unsplit(double low = 0.0, double high = 1.0);
18 | int uniform_int_unsplit(int low, int high);
19 | double normal_unsplit(double mu = 0.0, double sigma = 1.0);
20 | int poisson_int_unsplit(double mu);
21 | } // namespace RNG
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/include/serialization.hpp:
--------------------------------------------------------------------------------
1 | #ifndef SERIALIZATION_HPP_
2 | #define SERIALIZATION_HPP_
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 |
10 | /// Serialization and deserialization routines for msgpack
11 | ///
12 | /// FiberContainerBase NOT implemented (screws up everything)
13 | /// FiberContainerFiniteDifference implemented
14 |
15 | /// @brief Custom serializaiton and deserialization of the unique_ptr for FiberContainerBase
16 | namespace msgpack {
17 |
18 | MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) {
19 |
20 | namespace adaptor {
21 |
22 | template <>
23 | struct convert> {
24 | msgpack::object const &operator()(msgpack::object const &o, std::unique_ptr &v) const {
25 | // Sanity check on the contents of the unique pointer
26 | if (o.type != msgpack::type::ARRAY) {
27 | throw msgpack::type_error();
28 | }
29 |
30 | FiberContainerBase::FIBERTYPE fiber_type;
31 | o.via.array.ptr[0].convert(fiber_type);
32 |
33 | switch (fiber_type) {
34 | case FiberContainerBase::FIBERTYPE::FiniteDifference: {
35 | FiberContainerFiniteDifference fc_finitediff;
36 | o.convert(fc_finitediff);
37 | v = std::make_unique(std::move(fc_finitediff));
38 | break;
39 | }
40 | default:
41 | throw msgpack::type_error();
42 | } // switch
43 | return o;
44 | }
45 | };
46 |
47 | template <>
48 | struct pack> {
49 | template
50 | msgpack::packer &operator()(msgpack::packer &o,
51 | std::unique_ptr const &v) const {
52 | if (v->fiber_type_ == FiberContainerBase::FIBERTYPE::FiniteDifference) {
53 | o.pack(*static_cast(v.get()));
54 | }
55 |
56 | return o;
57 | }
58 | };
59 |
60 | } // namespace adaptor
61 | } // MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS)
62 |
63 | } // namespace msgpack
64 |
65 | #endif
66 |
67 |
--------------------------------------------------------------------------------
/include/skelly_sim.hpp:
--------------------------------------------------------------------------------
1 | #ifndef SKELLYSIM_HPP
2 | #define SKELLYSIM_HPP
3 |
4 | #ifndef SKELLYSIM_VERSION
5 | #define SKELLYSIM_VERSION ""
6 | #endif
7 |
8 | #ifndef SKELLYSIM_COMMIT
9 | #define SKELLYSIM_COMMIT ""
10 | #endif
11 |
12 | #include
13 |
14 | #include
15 |
16 | #include
17 | #define EIGEN_MATRIX_PLUGIN "eigen_matrix_plugin.h"
18 | #define EIGEN_QUATERNION_PLUGIN "eigen_quaternion_plugin.h"
19 |
20 | #include
21 | #include
22 | #include
23 |
24 | typedef Eigen::Map VectorMap;
25 | typedef Eigen::Map CVectorMap;
26 | typedef Eigen::Map ArrayMap;
27 | typedef Eigen::Map CArrayMap;
28 | typedef Eigen::Map MatrixMap;
29 | typedef Eigen::Map CMatrixMap;
30 | typedef Eigen::Ref ArrayRef;
31 | typedef const Eigen::Ref CArrayRef;
32 | typedef Eigen::Ref VectorRef;
33 | typedef const Eigen::Ref CVectorRef;
34 | typedef Eigen::Ref MatrixRef;
35 | typedef const Eigen::Ref CMatrixRef;
36 |
37 | /// Struct of parameters for exponentially decaying fiber-periphery interaction
38 | typedef struct {
39 | double f_0; ///< strength of interaction
40 | double l_0; ///< characteristic length of interaction
41 | } fiber_periphery_interaction_t;
42 |
43 | typedef struct {
44 | bool active; ///< If periphery binding enabled
45 | double polar_angle_start; ///< Angle from z axis where binding starts
46 | double polar_angle_end; ///< Angle from z axis where binding ends
47 | double threshold; ///< Minimum distance from cortex where binding will begin
48 | } periphery_binding_t;
49 |
50 | template
51 | struct ActiveIterator {
52 | using iterator_category = std::forward_iterator_tag;
53 | using difference_type = std::ptrdiff_t;
54 | using value_type = T;
55 | using pointer = value_type *;
56 | using reference = value_type &;
57 |
58 | ActiveIterator(int index, std::vector &objs) : m_index(index), container_ref(objs) {
59 | while (m_index < container_ref.size() && !container_ref[m_index].active()) {
60 | m_index++;
61 | }
62 | }
63 |
64 | reference operator*() const { return container_ref[m_index]; }
65 | pointer operator->() { return &container_ref[m_index]; }
66 |
67 | // Prefix increment
68 | ActiveIterator &operator++() {
69 | do {
70 | m_index++;
71 | } while (m_index < container_ref.size() && !container_ref[m_index].active());
72 |
73 | return *this;
74 | }
75 |
76 | // Postfix increment
77 | ActiveIterator operator++(int) {
78 | ActiveIterator tmp = *this;
79 | ++(*this);
80 | return tmp;
81 | }
82 |
83 | friend bool operator==(const ActiveIterator &a, const ActiveIterator &b) { return a.m_index == b.m_index; };
84 | friend bool operator!=(const ActiveIterator &a, const ActiveIterator &b) { return a.m_index != b.m_index; };
85 |
86 | private:
87 | int m_index;
88 | std::vector &container_ref;
89 | };
90 |
91 | #endif
92 |
--------------------------------------------------------------------------------
/include/solver.hpp:
--------------------------------------------------------------------------------
1 | #ifndef SOLVER_HPP
2 | #define SOLVER_HPP
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | template
11 | class Solver {
12 | public:
13 | typedef typename matvec_T::scalar_type ST;
14 | typedef Tpetra::Vector
16 | SV;
17 | typedef typename matvec_T::MV MV;
18 | typedef Tpetra::Operator OP;
19 |
20 | Solver() {
21 | comm_ = Tpetra::getDefaultComm();
22 | matvec_ = rcp(new matvec_T(comm_));
23 | preconditioner_ = rcp(new precond_T(comm_));
24 | map_ = matvec_->getDomainMap();
25 | X_ = rcp(new SV(map_));
26 | RHS_ = rcp(new SV(map_));
27 | };
28 | void set_RHS();
29 | bool solve();
30 | void apply_preconditioner();
31 | CVectorMap get_solution() { return CVectorMap(X_->getData(0).getRawPtr(), X_->getLocalLength()); };
32 | double get_residual() {
33 | Teuchos::RCP Y(new SV(map_));
34 | matvec_->apply(*X_, *Y);
35 | CVectorMap RHS_map(RHS_->getData(0).getRawPtr(), RHS_->getLocalLength());
36 | CVectorMap Y_map(Y->getData(0).getRawPtr(), Y->getLocalLength());
37 | double residual = (RHS_map - Y_map).squaredNorm();
38 | MPI_Allreduce(MPI_IN_PLACE, &residual, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
39 | return sqrt(residual);
40 | }
41 |
42 | private:
43 | Teuchos::RCP> comm_;
44 | Teuchos::RCP preconditioner_;
45 | Teuchos::RCP matvec_;
46 | Teuchos::RCP X_;
47 | Teuchos::RCP RHS_;
48 | Teuchos::RCP> map_;
49 | };
50 |
51 | #endif
52 |
--------------------------------------------------------------------------------
/include/solver_hydro.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | class P_inv_hydro : public Tpetra::Operator<> {
11 | public:
12 | // Tpetra::Operator subclasses should always define these four typedefs.
13 | typedef Tpetra::Operator<>::scalar_type scalar_type;
14 | typedef Tpetra::Operator<>::local_ordinal_type local_ordinal_type;
15 | typedef Tpetra::Operator<>::global_ordinal_type global_ordinal_type;
16 | typedef Tpetra::Operator<>::node_type node_type;
17 | // The type of the input and output arguments of apply().
18 | typedef Tpetra::MultiVector MV;
19 | // The Map specialization used by this class.
20 | typedef Tpetra::Map map_type;
21 |
22 | private:
23 | // This is an implementation detail; users don't need to see it.
24 | typedef Tpetra::Import import_type;
25 |
26 | public:
27 | // comm: The communicator over which to distribute those rows and columns.
28 | P_inv_hydro(const Teuchos::RCP> comm);
29 | virtual ~P_inv_hydro() {}
30 |
31 | Teuchos::RCP getDomainMap() const { return opMap_; };
32 | Teuchos::RCP getRangeMap() const { return opMap_; };
33 |
34 | void apply(const MV &X, MV &Y, Teuchos::ETransp mode = Teuchos::NO_TRANS,
35 | scalar_type alpha = Teuchos::ScalarTraits::one(),
36 | scalar_type beta = Teuchos::ScalarTraits::zero()) const;
37 |
38 | private:
39 | Teuchos::RCP opMap_;
40 | Teuchos::RCP> comm_;
41 | const int rank_;
42 | };
43 |
44 | class A_fiber_hydro : public Tpetra::Operator<> {
45 | public:
46 | // Tpetra::Operator subclasses should always define these four typedefs.
47 | typedef Tpetra::Operator<>::scalar_type scalar_type;
48 | typedef Tpetra::Operator<>::local_ordinal_type local_ordinal_type;
49 | typedef Tpetra::Operator<>::global_ordinal_type global_ordinal_type;
50 | typedef Tpetra::Operator<>::node_type node_type;
51 | // The type of the input and output arguments of apply().
52 | typedef Tpetra::MultiVector MV;
53 | // The Map specialization used by this class.
54 | typedef Tpetra::Map map_type;
55 |
56 | private:
57 | // This is an implementation detail; users don't need to see it.
58 | typedef Tpetra::Import import_type;
59 |
60 | public:
61 | // comm: The communicator over which to distribute those rows and columns.
62 | A_fiber_hydro(const Teuchos::RCP> comm);
63 | virtual ~A_fiber_hydro() {}
64 | Teuchos::RCP getDomainMap() const { return opMap_; };
65 | Teuchos::RCP getRangeMap() const { return opMap_; };
66 |
67 | void apply(const MV &X, MV &Y, Teuchos::ETransp mode = Teuchos::NO_TRANS,
68 | scalar_type alpha = Teuchos::ScalarTraits::one(),
69 | scalar_type beta = Teuchos::ScalarTraits::zero()) const;
70 |
71 | private:
72 | Teuchos::RCP opMap_;
73 | Teuchos::RCP> comm_;
74 | const int rank_;
75 | };
76 |
--------------------------------------------------------------------------------
/include/streamline.hpp:
--------------------------------------------------------------------------------
1 | #ifndef STREAMLINE_HPP
2 | #define STREAMLINE_HPP
3 |
4 | #include
5 |
6 | #include
7 |
8 | /// @brief Class representing a streamline
9 | class StreamLine {
10 | public:
11 | Eigen::MatrixXd x; ///< Coordinates of streamline
12 | Eigen::MatrixXd val; ///< Velocities of streamline
13 | Eigen::VectorXd time; ///< Evaluation times
14 | virtual void compute(); ///< Compute streamline
15 |
16 | StreamLine() = default;
17 |
18 | StreamLine(CVectorRef &x0_, double dt_init_, double t_final_, double abs_err_, double rel_err_,
19 | bool back_integrate_)
20 | : x(x0_), dt_init(dt_init_), t_final(t_final_), abs_err(abs_err_), rel_err(rel_err_),
21 | back_integrate(back_integrate_) {
22 | compute();
23 | };
24 |
25 | void write(std::ofstream &ofs) {
26 | msgpack::pack(ofs, *this);
27 | ofs.flush();
28 | };
29 | MSGPACK_DEFINE_MAP(x, val, time);
30 |
31 | protected:
32 | double dt_init;
33 | double t_final;
34 | double abs_err;
35 | double rel_err;
36 | bool back_integrate;
37 | };
38 |
39 | class VortexLine : public StreamLine {
40 | public:
41 | VortexLine() = default;
42 |
43 | VortexLine(CVectorRef &x0_, double dt_init_, double t_final_, double abs_err_, double rel_err_,
44 | bool back_integrate_) {
45 | x = x0_;
46 | dt_init = dt_init_;
47 | t_final = t_final_;
48 | abs_err = abs_err_;
49 | rel_err = rel_err_;
50 | back_integrate = back_integrate_;
51 | compute();
52 | };
53 |
54 | virtual void compute() override; ///< Compute VortexLine
55 | MSGPACK_DEFINE_MAP(x, val, time);
56 | };
57 |
58 | #endif
59 |
--------------------------------------------------------------------------------
/include/system.hpp:
--------------------------------------------------------------------------------
1 | #ifndef SYSTEM_HPP
2 | #define SYSTEM_HPP
3 |
4 | #include
5 | #include
6 |
7 | class Params;
8 | class BodyContainer;
9 | class FiberContainerBase;
10 | class Periphery;
11 | class PointSourceContainer;
12 |
13 | /// Namespace for System, which drives the simulation and handles communication (timestepping, data wrangling, etc)
14 | namespace System {
15 |
16 | /// @brief Time varying system properties that are extrinsic to the physical objects
17 | struct properties_t {
18 | double dt; ///< Current timestep size
19 | double time = 0.0; ///< Current system time
20 | };
21 |
22 | void init(const std::string &input_file, bool resume_flag = false, bool listen_flag = false);
23 | Params *get_params();
24 | BodyContainer *get_body_container();
25 | FiberContainerBase *get_fiber_container();
26 | Periphery *get_shell();
27 | PointSourceContainer *get_point_source_container();
28 | toml::value *get_param_table();
29 |
30 | Eigen::MatrixXd calculate_body_fiber_link_conditions(CVectorRef &fibers_xt, CVectorRef &x_bodies);
31 | std::tuple get_local_solution_sizes();
32 | Eigen::VectorXd apply_preconditioner(CVectorRef &x);
33 | Eigen::VectorXd apply_matvec(CVectorRef &x);
34 | void dynamic_instability();
35 | void prep_state_for_solver();
36 | bool solve();
37 | bool step();
38 | void run();
39 | void write();
40 | void write(std::ofstream &);
41 | void write_header(std::ofstream &);
42 | bool check_collision();
43 | void backup();
44 | void restore();
45 | void set_evaluator(const std::string &evaluator);
46 |
47 | Eigen::VectorXd get_fiber_RHS();
48 | Eigen::VectorXd get_shell_RHS();
49 | Eigen::VectorXd get_body_RHS();
50 | struct properties_t &get_properties();
51 | Eigen::VectorXd &get_curr_solution();
52 | std::tuple get_solution_maps(double *x);
53 | std::tuple get_solution_maps(const double *x);
54 | Eigen::MatrixXd velocity_at_targets(CMatrixRef &r_trg);
55 |
56 | }; // namespace System
57 |
58 | #endif
59 |
--------------------------------------------------------------------------------
/include/trajectory_reader.hpp:
--------------------------------------------------------------------------------
1 | #ifndef TRAJECTORY_READER_HPP
2 | #define TRAJECTORY_READER_HPP
3 |
4 | #include
5 |
6 | #include
7 |
8 | class TrajectoryReader {
9 | public:
10 | TrajectoryReader(const std::string &input_file, bool resume_flag);
11 | std::size_t read_next_frame();
12 | bool load_frame(std::size_t frameno);
13 | void unpack_current_frame(bool silence_output = false);
14 | std::size_t get_n_frames() const { return index.offsets.size(); }
15 |
16 | private:
17 | int fd_; ///< File descriptor
18 | std::size_t buflen_; ///< size of our file
19 | char *addr_; ///< mmap address
20 | std::size_t offset_; ///< current byte location in trajectory
21 | std::size_t header_offset_; ///< header offset size in trajectory
22 | msgpack::object_handle oh_; ///< handle to last read frame
23 | bool resume_flag_; ///< Reader being used to load resume data
24 | long int mtime;
25 |
26 | struct {
27 | long int mtime;
28 | std::vector offsets; ///< Vector of offsets for each frame
29 | std::vector times; ///< Vector of times for each frame
30 | MSGPACK_DEFINE_MAP(mtime, offsets, times);
31 | } index;
32 |
33 | void read_header();
34 | void load_index(const std::string &traj_file);
35 | void build_index(const std::string &index_file);
36 | };
37 |
38 | #endif
39 |
--------------------------------------------------------------------------------
/include/utils.hpp:
--------------------------------------------------------------------------------
1 | #ifndef UTILS_HPP
2 | #define UTILS_HPP
3 |
4 | #include
5 |
6 | namespace cnpy {
7 | struct NpyArray;
8 | using npz_t = std::map;
9 | } // namespace cnpy
10 |
11 | namespace utils {
12 | Eigen::MatrixXd barycentric_matrix(CArrayRef &x, CArrayRef &y);
13 | Eigen::MatrixXd finite_diff(CArrayRef &s, int M, int n_s);
14 | Eigen::VectorXd collect_into_global(CVectorRef &local_vec);
15 |
16 | Eigen::MatrixXd load_mat(cnpy::npz_t &npz, const char *var);
17 | Eigen::VectorXd load_vec(cnpy::npz_t &npz, const char *var);
18 |
19 | template
20 | bool allclose(
21 | const Eigen::DenseBase &a, const Eigen::DenseBase &b,
22 | const typename DerivedA::RealScalar &rtol = Eigen::NumTraits::dummy_precision(),
23 | const typename DerivedA::RealScalar &atol = Eigen::NumTraits::epsilon()) {
24 | return ((a.derived() - b.derived()).array().abs() <= (atol + rtol * b.derived().array().abs())).all();
25 | }
26 |
27 | class LoggerRedirect {
28 | public:
29 | LoggerRedirect(std::ostream &in) : m_orig(in), m_old_buffer(in.rdbuf(ss.rdbuf())) {}
30 |
31 | ~LoggerRedirect() { m_orig.rdbuf(m_old_buffer); }
32 |
33 | void flush(spdlog::level::level_enum level, std::string logger = "status") {
34 | std::istringstream dumbtmp(ss.str());
35 | for (std::string line; std::getline(dumbtmp, line);)
36 | spdlog::get(logger)->log(level, line);
37 | ss.str("");
38 | ss.clear();
39 | }
40 |
41 | private:
42 | LoggerRedirect(const LoggerRedirect &);
43 | LoggerRedirect &operator=(const LoggerRedirect &);
44 |
45 | std::stringstream ss; ///< temporary object to redirect to for lifetime of this object
46 | std::ostream &m_orig; ///< Actual original ostream object (usually cout)
47 | std::streambuf *m_old_buffer; ///< pointer to original streambuf pointer (usually cout's)
48 | };
49 |
50 | }; // namespace utils
51 |
52 | #endif
53 |
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/EPS/SkellySim_Icon_CMYK_Black.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Icon/EPS/SkellySim_Icon_CMYK_Black.eps
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/EPS/SkellySim_Icon_CMYK_Full.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Icon/EPS/SkellySim_Icon_CMYK_Full.eps
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/EPS/SkellySim_Icon_CMYK_White.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Icon/EPS/SkellySim_Icon_CMYK_White.eps
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/JPG/SkellySim_Icon_CMYK_Black.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Icon/JPG/SkellySim_Icon_CMYK_Black.jpg
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/JPG/SkellySim_Icon_CMYK_Full.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Icon/JPG/SkellySim_Icon_CMYK_Full.jpg
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/PDF/SkellySim_Icon_CMYK_Black.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Icon/PDF/SkellySim_Icon_CMYK_Black.pdf
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/PDF/SkellySim_Icon_CMYK_Full.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Icon/PDF/SkellySim_Icon_CMYK_Full.pdf
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/PNG/SkellySim_Icon_CMYK_Black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Icon/PNG/SkellySim_Icon_CMYK_Black.png
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/PNG/SkellySim_Icon_CMYK_Full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Icon/PNG/SkellySim_Icon_CMYK_Full.png
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/PNG/SkellySim_Icon_CMYK_White.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Icon/PNG/SkellySim_Icon_CMYK_White.png
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/SVG/SkellySim_Icon_CMYK_Black.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/SVG/SkellySim_Icon_CMYK_Full.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/logos/CMYK/Icon/SVG/SkellySim_Icon_CMYK_White.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/EPS/SkellySim_Logo_CMYK_Black.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Logo/EPS/SkellySim_Logo_CMYK_Black.eps
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/EPS/SkellySim_Logo_CMYK_Full.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Logo/EPS/SkellySim_Logo_CMYK_Full.eps
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/EPS/SkellySim_Logo_CMYK_White.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Logo/EPS/SkellySim_Logo_CMYK_White.eps
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/JPG/SkellySim_Logo_CMYK_Black.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Logo/JPG/SkellySim_Logo_CMYK_Black.jpg
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/JPG/SkellySim_Logo_CMYK_Full.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Logo/JPG/SkellySim_Logo_CMYK_Full.jpg
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/PDF/SkellySim_Logo_CMYK_Black.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Logo/PDF/SkellySim_Logo_CMYK_Black.pdf
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/PDF/SkellySim_Logo_CMYK_Full.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Logo/PDF/SkellySim_Logo_CMYK_Full.pdf
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/PNG/SkellySim_Logo_CMYK_Black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Logo/PNG/SkellySim_Logo_CMYK_Black.png
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/PNG/SkellySim_Logo_CMYK_Full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Logo/PNG/SkellySim_Logo_CMYK_Full.png
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/PNG/SkellySim_Logo_CMYK_White.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/CMYK/Logo/PNG/SkellySim_Logo_CMYK_White.png
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/SVG/SkellySim_Logo_CMYK_Black.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/SVG/SkellySim_Logo_CMYK_Full.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/logos/CMYK/Logo/SVG/SkellySim_Logo_CMYK_White.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/logos/Favicon/SkellySim_Favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/Favicon/SkellySim_Favicon.ico
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/EPS/SkellySim_Icon_RGB_Black.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Icon/EPS/SkellySim_Icon_RGB_Black.eps
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/EPS/SkellySim_Icon_RGB_Full.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Icon/EPS/SkellySim_Icon_RGB_Full.eps
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/EPS/SkellySim_Icon_RGB_White.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Icon/EPS/SkellySim_Icon_RGB_White.eps
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/JPG/SkellySim_Icon_RGB_Black.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Icon/JPG/SkellySim_Icon_RGB_Black.jpg
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/JPG/SkellySim_Icon_RGB_Full.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Icon/JPG/SkellySim_Icon_RGB_Full.jpg
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/PDF/SkellySim_Icon_RGB_Black.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Icon/PDF/SkellySim_Icon_RGB_Black.pdf
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/PDF/SkellySim_Icon_RGB_Full.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Icon/PDF/SkellySim_Icon_RGB_Full.pdf
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/PNG/SkellySim_Icon_RGB_Black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Icon/PNG/SkellySim_Icon_RGB_Black.png
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/PNG/SkellySim_Icon_RGB_Full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Icon/PNG/SkellySim_Icon_RGB_Full.png
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/PNG/SkellySim_Icon_RGB_White.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Icon/PNG/SkellySim_Icon_RGB_White.png
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/SVG/SkellySim_Icon_RGB_Black.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/SVG/SkellySim_Icon_RGB_Full.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/logos/RGB/Icon/SVG/SkellySim_Icon_RGB_White.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/EPS/SkellySim_Logo_RGB_Black.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Logo/EPS/SkellySim_Logo_RGB_Black.eps
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/EPS/SkellySim_Logo_RGB_Full.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Logo/EPS/SkellySim_Logo_RGB_Full.eps
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/EPS/SkellySim_Logo_RGB_White.eps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Logo/EPS/SkellySim_Logo_RGB_White.eps
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/JPG/SkellySim_Logo_RGB_Black.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Logo/JPG/SkellySim_Logo_RGB_Black.jpg
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/JPG/SkellySim_Logo_RGB_Full.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Logo/JPG/SkellySim_Logo_RGB_Full.jpg
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/PDF/SkellySim_Logo_RGB_Black.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Logo/PDF/SkellySim_Logo_RGB_Black.pdf
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/PDF/SkellySim_Logo_RGB_Full.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Logo/PDF/SkellySim_Logo_RGB_Full.pdf
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/PNG/SkellySim_Logo_RGB_Black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Logo/PNG/SkellySim_Logo_RGB_Black.png
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/PNG/SkellySim_Logo_RGB_Full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Logo/PNG/SkellySim_Logo_RGB_Full.png
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/PNG/SkellySim_Logo_RGB_White.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/misc/logos/RGB/Logo/PNG/SkellySim_Logo_RGB_White.png
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/SVG/SkellySim_Logo_RGB_Black.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/SVG/SkellySim_Logo_RGB_Full.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/logos/RGB/Logo/SVG/SkellySim_Logo_RGB_White.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/misc/module_template.in:
--------------------------------------------------------------------------------
1 | #%Module1.0#####################################################################
2 | #
3 | # Module: skelly_sim ${SKELLYSIM_VERSION}
4 | #
5 |
6 | set pkg skelly_sim
7 | set root ${CMAKE_INSTALL_PREFIX}
8 | set version ${SKELLYSIM_VERSION}
9 |
10 | proc ModulesHelp { } {
11 | puts stderr "Sets the environment for $pkg-$version"
12 | }
13 |
14 | module-whatis "Sets the environment for $pkg-$version"
15 |
16 | prepend-path PATH $root/bin
17 | setenv SKELLYSIM_BASE $root
18 | setenv SKELLYSIM_VERSION $version
19 |
--------------------------------------------------------------------------------
/notes/model_changes.md:
--------------------------------------------------------------------------------
1 | # Numeric differences between this and Gokberk's version
2 |
3 | * Slenderness parameter 'epsilon' (r/L)
4 | * Value was fixed in original simulations (usually 1E-3)
5 | * Now it varies as it should (r/L)
6 | * Fix issue with stresslet improperly scaling inversely with viscosity
7 | Wasn't a problem if eta=1, but otherwise both SkellySim and its
8 | predecessor were wrong. Fortunately we always used eta=1.
9 |
--------------------------------------------------------------------------------
/notes/model_overview.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/notes/model_overview.pdf
--------------------------------------------------------------------------------
/notes/planning.md:
--------------------------------------------------------------------------------
1 | # Issues
2 | 1. Close Eval (for surfaces)
3 | 2. Close Eval (for fibers) ???
4 | 3. Fiber Discretization
5 | 4. Fictitious ??? motion caused by ??? (timestepping scheme)
6 | 5. Boundary condition types (on fiber, on fluid)
7 | 6. Integration for different timestepping schemes
8 | 7. Do we have background flows?
9 | 8. Better scheme for bodies (stability)
10 | 9. Better for system for initial condition perturbation
11 | 10. Other formulations (Donev/Maxian/Ohm/Tangent-angle type formulation)
12 | 11. Stealth swimmers
13 | 12. Motors !?!?!&$@$
14 | 13. ~Background flows~
15 | 14. Rotate LU decomposition for bodies rather than re-decompose
16 |
17 | # Test problems:
18 | 1. PDE solver tests for exterior confinement // mobile bodies in confinement
19 | * easy for exterior; probably easy enough for exterior/interior
20 | * refinement for harder problems
21 | * robert has done approaching a periphery but not rotating
22 | 2. Single fiber refinement studies (correct accuracy in space / time)
23 | * free fibers
24 | * clamped fibers
25 | * pinned & growing fibers
26 | 3. Many fiber refinement studies
27 | * body with interacting fibers
28 | * ... lots of things we can imagine
29 | 4. Things with known solutions ---
30 | * single fiber under load (oscillation frequency /-> RFT)
31 | * ehssans oscillatory rheology computations
32 | * slow rotation with single MT on body (asymptotic calculation)
33 | 5. Stress tests
34 | * fibers aligned along extensional / compressional / shear flows
35 | * Reza's example
36 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "skelly_sim"
3 | version = "0.9.12"
4 | authors = [
5 | { name="Robert Blackwell", email="rblackwell@flatironinstitute.org" }
6 | ]
7 | description = 'Simulate cytoskeletal systems with full hydrodynamics'
8 | readme = "README.md"
9 | requires-python = ">=3.7"
10 | classifiers = [
11 | 'Development Status :: 4 - Beta',
12 | 'Intended Audience :: Scientists/Mathematicians',
13 | 'License :: Apache 2',
14 | 'Programming Language :: Python :: 3',
15 | ]
16 | license = { file="LICENSE" }
17 | dependencies = [
18 | 'numba',
19 | 'numpy',
20 | 'scipy',
21 | 'msgpack',
22 | 'scikit-learn',
23 | 'toml',
24 | 'packaging',
25 | 'matplotlib',
26 | 'dataclass_utils',
27 | 'nptyping',
28 | 'function_generator@git+https://github.com/blackwer/function_generator@fda8b44b5edf15d1677ca7433139a43778d6659d',
29 | ]
30 |
31 | [project.optional-dependencies]
32 | tests = [
33 | 'pytest',
34 | ]
35 | docs = [
36 | 'sphinx-book-theme'
37 | ]
38 |
39 | [project.urls]
40 | homepage = "https//github.com/flatironinstitute/skelly_sim"
41 | documentation = "https://users.flatironinstitute.org/~rblackwell/py-skellysim"
42 | developer-documentation = "https://users.flatirninstitute.org/~rblackwell/skellysim"
43 |
44 | [project.scripts]
45 | skelly_precompute = "skelly_sim.precompute:main"
46 |
47 | [build-system]
48 | requires = ["flit_core >=3.2,<4"]
49 | build-backend = "flit_core.buildapi"
50 |
51 | [pytest]
52 | pythonpath = "src"
53 |
--------------------------------------------------------------------------------
/scripts/skelly.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/scripts/skelly.blend
--------------------------------------------------------------------------------
/scripts/skelly_sim.def:
--------------------------------------------------------------------------------
1 | BootStrap: library
2 | From: ubuntu:21.04
3 |
4 | %post
5 | export MKLROOT=/opt/intel/oneapi/mkl/latest
6 | export CXXFLAGS_="-I/usr/lib/x86_64-linux-gnu/openmpi/include -mavx2 -mfma"
7 | SKELLY_VERS="v0.9.9"
8 |
9 | apt -y update
10 | apt -y install curl gpg software-properties-common
11 |
12 | curl -s https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor > /usr/share/keyrings/oneapi-archive-keyring.gpg
13 | echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" > /etc/apt/sources.list.d/oneAPI.list
14 |
15 | apt-add-repository multiverse
16 | apt-add-repository universe
17 |
18 | apt -y update
19 | apt -y install intel-oneapi-mkl-devel build-essential libopenmpi-dev cmake libeigen3-dev ninja-build git python3-pip
20 |
21 | pip3 install git+https://github.com/flatironinstitute/SkellySim.git@$SKELLY_VERS
22 |
23 | mkdir build
24 | cd build
25 |
26 | curl -L -o pvfmm.tar.gz https://github.com/dmalhotra/pvfmm/archive/refs/tags/v1.2.1.tar.gz
27 | tar -xf pvfmm.tar.gz
28 | mkdir pvfmm-build
29 |
30 | cd pvfmm-build
31 | cmake ../pvfmm-1.2.1 -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release \
32 | -DCMAKE_CXX_FLAGS="$CXXFLAGS_" -GNinja
33 | ninja
34 | ninja install
35 | cd ..
36 |
37 | curl -L -o stkfmm.tar.gz https://github.com/wenyan4work/STKFMM/archive/refs/tags/v1.0.0.tar.gz
38 | tar -xf stkfmm.tar.gz
39 | mkdir stkfmm-build
40 |
41 | cd stkfmm-build
42 | pvfmm_DIR=/usr/local/share/pvfmm cmake ../STKFMM-1.0.0 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local \
43 | -DCMAKE_CXX_FLAGS="$CXXFLAGS_" -GNinja
44 | ninja
45 | ninja install
46 | cd ..
47 |
48 | curl -L -o trilinos.tar.gz https://github.com/trilinos/Trilinos/archive/refs/tags/trilinos-release-13-2-0.tar.gz
49 | tar -xf trilinos.tar.gz
50 | mkdir trilinos-build
51 |
52 | cd trilinos-build
53 | cmake ../Trilinos-trilinos-release-13-2-0 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local -DTrilinos_VERBOSE_CONFIGURE:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=ON -DCMAKE_CXX_STANDARD:STRING=14 -DTrilinos_ENABLE_ALL_OPTIONAL_PACKAGES:BOOL=OFF -DTrilinos_ENABLE_ALL_PACKAGES:BOOL=OFF -DTrilinos_ENABLE_OpenMP=ON -DTrilinos_ENABLE_Kokkos:BOOL=ON -DTrilinos_ENABLE_Belos:BOOL=ON -DTrilinos_ENABLE_Tpetra:BOOL=ON -DTrilinos_ENABLE_Teuchos:BOOL=ON -DBUILD_TESTING:BOOL=OFF -DTrilinos_ENABLE_Fortran:BOOL=OFF -D TPL_ENABLE_MPI=ON -DTPL_BLAS_LIBRARIES=/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_rt.so -DTPL_LAPACK_LIBRARIES=/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_rt.so -DCMAKE_CXX_FLAGS="$CXXFLAGS_" -GNinja
54 | ninja
55 | ninja install
56 | cd ..
57 |
58 | git clone https://github.com/flatironinstitute/skellysim
59 | cd skellysim
60 | git checkout ${SKELLY_VERS}
61 | git submodule update --init
62 | cd ..
63 |
64 | mkdir skellysim-build
65 | cd skellysim-build
66 | STKFMM_ROOT=/usr/local cmake ../skellysim -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local \
67 | -DCMAKE_CXX_FLAGS="$CXXFLAGS_" -DBLAS_LIBRARIES=$MKLROOT/lib/intel64/libmkl_rt.so \
68 | -DLAPACK_LIBRARIES=$MKLROOT/lib/intel64/libmkl_rt.so -GNinja
69 | ninja
70 | ninja install
71 |
72 | cd ../..
73 | rm -rf build
74 |
75 |
76 | %environment
77 | export LC_ALL=C
78 | export PATH=/usr/local/bin:$PATH
79 | export MKL_THREADING_LAYER=GNU
80 | export pvfmm_DIR=$HOME/.cache/pvfmm
81 | export LD_LIBRARY_PATH=/opt/intel/oneapi/mkl/latest/lib/intel64:/usr/local/lib:$LD_LIBRARY_PATH
82 |
83 | %labels
84 | Author blackwer
85 |
--------------------------------------------------------------------------------
/src/core/background_source.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 |
5 | BackgroundSource::BackgroundSource(const toml::value &background_table) {
6 | if (background_table.contains("components"))
7 | components_ = parse_util::convert_array(background_table.at("components").as_array());
8 | if (background_table.contains("scale_factor"))
9 | scale_factor_ = parse_util::convert_array<>(background_table.at("scale_factor").as_array());
10 | if (background_table.contains("uniform"))
11 | uniform_ = parse_util::convert_array<>(background_table.at("uniform").as_array());
12 | }
13 |
14 | Eigen::MatrixXd BackgroundSource::flow(const CMatrixRef &r_trg, double eta) {
15 | Eigen::MatrixXd vel = Eigen::MatrixXd::Zero(3, r_trg.cols());
16 |
17 | for (int i = 0; i < r_trg.cols(); ++i)
18 | for (int j = 0; j < 3; ++j)
19 | vel(j, i) = uniform_[j] + r_trg(components_[j], i) * scale_factor_[j];
20 |
21 | return vel;
22 | }
23 |
--------------------------------------------------------------------------------
/src/core/body.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | using Eigen::MatrixXd;
11 | using Eigen::VectorXd;
12 |
13 | /// @brief Construct body from relevant toml config and system params
14 | ///
15 | /// @param[in] body_table toml table from pre-parsed config
16 | /// @param[in] params Pre-constructed Params object
17 | /// surface).
18 | /// @return Body object that has been appropriately rotated. Other internal cache variables are _not_ updated.
19 | /// @see update_cache_variables
20 | Body::Body(const toml::value &body_table, const Params ¶ms) {}
21 |
22 | const std::string Body::EXTFORCE_name[] = {"Linear", "Oscillatory"};
23 |
24 |
--------------------------------------------------------------------------------
/src/core/body_deformable.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | void DeformableBody::min_copy(const std::shared_ptr &other) {}
14 | void DeformableBody::update_RHS(CMatrixRef &v_on_body) {}
15 | void DeformableBody::update_cache_variables(double eta) {}
16 | void DeformableBody::update_preconditioner(double eta) {}
17 | void DeformableBody::load_precompute_data(const std::string &input_file) {}
18 | void DeformableBody::step(double dt, CVectorRef &body_solution) {}
19 | Eigen::VectorXd DeformableBody::matvec(CMatrixRef &v_bodies, CVectorRef &body_solution) const {
20 | return Eigen::VectorXd();
21 | }
22 | Eigen::VectorXd DeformableBody::apply_preconditioner(CVectorRef &x) const { return Eigen::VectorXd(); }
23 | Eigen::Vector3d DeformableBody::get_position() const { return Eigen::Vector3d(); }
24 |
25 | bool DeformableBody::check_collision(const Periphery &periphery, double threshold) const {
26 | return periphery.check_collision(*this, threshold);
27 | }
28 | bool DeformableBody::check_collision(const Body &body, double threshold) const {
29 | return body.check_collision(*this, threshold);
30 | }
31 | bool DeformableBody::check_collision(const SphericalBody &body, double threshold) const {
32 | return body.check_collision(*this, threshold);
33 | }
34 | bool DeformableBody::check_collision(const DeformableBody &body, double threshold) const {
35 | spdlog::warn("check_collision not defined for DeformableBody->DeformableBody");
36 | return false;
37 | }
38 | bool DeformableBody::check_collision(const EllipsoidalBody &body, double threshold) const {
39 | spdlog::warn("check_collision not defined for DeformableBody->EllipsoidalBody");
40 | return false;
41 | }
42 |
--------------------------------------------------------------------------------
/src/core/fiber_container_base.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | /// @brief Constructor
8 | FiberContainerBase::FiberContainerBase(toml::array &fiber_tables, Params ¶ms) {
9 | spdlog::debug("FiberContainerBase::FiberContainerBase");
10 |
11 | MPI_Comm_size(MPI_COMM_WORLD, &world_size_);
12 | MPI_Comm_rank(MPI_COMM_WORLD, &world_rank_);
13 |
14 | set_evaluator(params.pair_evaluator);
15 |
16 | spdlog::debug("FiberContainerBase::FiberContainerBase return");
17 | }
18 |
19 | /// @brief Set the evaluator
20 | void FiberContainerBase::set_evaluator(const std::string &evaluator) {
21 | auto ¶ms = *System::get_params();
22 | if (evaluator == "FMM") {
23 | utils::LoggerRedirect redirect(std::cout);
24 | const int mult_order = params.stkfmm.fiber_stokeslet_multipole_order;
25 | const int max_pts = params.stkfmm.fiber_stokeslet_max_points;
26 | stokeslet_kernel_ = kernels::FMM(mult_order, max_pts, stkfmm::PAXIS::NONE,
27 | stkfmm::KERNEL::Stokes, kernels::stokes_vel_fmm);
28 | redirect.flush(spdlog::level::debug, "STKFMM");
29 | } else if (evaluator == "CPU")
30 | stokeslet_kernel_ = kernels::stokeslet_direct_cpu;
31 | else if (evaluator == "GPU")
32 | stokeslet_kernel_ = kernels::stokeslet_direct_gpu;
33 | }
34 |
35 | /// @brief Get the global number of fibers in all MPI ranks
36 | int FiberContainerBase::get_global_fiber_count() const {
37 | const int local_fib_count = get_local_fiber_count();
38 | int global_fib_count;
39 |
40 | MPI_Allreduce(&local_fib_count, &global_fib_count, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
41 | return global_fib_count;
42 | }
43 |
44 | /// @brief Get the global number of nodes for fibers in all MPI ranks
45 | int FiberContainerBase::get_global_node_count() const {
46 | const int local_node_count = get_local_node_count();
47 | int global_node_count;
48 |
49 | MPI_Allreduce(&local_node_count, &global_node_count, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
50 | return global_node_count;
51 | }
52 |
53 | /// @brief Get the global solution size across fibers in all MPI ranks
54 | int FiberContainerBase::get_global_solution_size() const {
55 | const int local_solution_size = get_local_solution_size();
56 | int global_solution_size;
57 |
58 | MPI_Allreduce(&local_solution_size, &global_solution_size, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
59 | return global_solution_size;
60 | }
61 |
--------------------------------------------------------------------------------
/src/core/point_source.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | PointSource::PointSource(const toml::value &point_table) {
6 | if (point_table.contains("position"))
7 | position_ = parse_util::convert_array<>(point_table.at("position").as_array());
8 | if (point_table.contains("force"))
9 | force_ = parse_util::convert_array<>(point_table.at("force").as_array());
10 | if (point_table.contains("torque"))
11 | torque_ = parse_util::convert_array<>(point_table.at("torque").as_array());
12 | if (point_table.contains("time_to_live"))
13 | time_to_live_ = point_table.at("time_to_live").as_floating();
14 | }
15 |
16 | Eigen::MatrixXd PointSourceContainer::flow(const CMatrixRef &r_trg, double eta, double time) {
17 | Eigen::MatrixXd vel = Eigen::MatrixXd::Zero(3, r_trg.cols());
18 | std::vector> forcers;
19 | std::vector> torquers;
20 |
21 | for (auto &point : points) {
22 | // Check if deactivated. ttl of 0.0 implies always alive
23 | if (point.time_to_live_ && time >= point.time_to_live_)
24 | continue;
25 | if (point.force_.any())
26 | forcers.push_back(std::make_pair(point.position_, point.force_));
27 | if (point.torque_.any())
28 | torquers.push_back(std::make_pair(point.position_, point.torque_));
29 | }
30 |
31 | if (forcers.size()) {
32 | const int n_forcers = forcers.size();
33 | Eigen::MatrixXd positions(3, n_forcers);
34 | Eigen::MatrixXd forces(3, n_forcers);
35 | for (int i = 0; i < n_forcers; ++i) {
36 | positions.col(i) = forcers[i].first;
37 | forces.col(i) = forcers[i].second;
38 | }
39 | vel += kernels::oseen_tensor_contract_direct(positions, r_trg, forces, eta);
40 | }
41 |
42 | if (torquers.size()) {
43 | const int n_torquers = torquers.size();
44 | Eigen::MatrixXd positions(3, n_torquers);
45 | Eigen::MatrixXd forces(3, n_torquers);
46 | for (int i = 0; i < n_torquers; ++i) {
47 | positions.col(i) = torquers[i].first;
48 | forces.col(i) = torquers[i].second;
49 | }
50 | vel += kernels::rotlet(positions, r_trg, forces, eta);
51 | }
52 |
53 | return vel;
54 | }
55 |
56 | PointSourceContainer::PointSourceContainer(const toml::array &point_tables) {
57 | const int n_points_tot = point_tables.size();
58 | spdlog::info("Reading in {} points", n_points_tot);
59 |
60 | for (int i_point = 0; i_point < n_points_tot; ++i_point) {
61 | const toml::value &point_table = point_tables.at(i_point);
62 | points.emplace_back(PointSource(point_table));
63 |
64 | auto &point = points.back();
65 | auto position = point.position_;
66 | spdlog::info("Point {}: [ {}, {}, {} ]", i_point, position[0], position[1], position[2]);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/core/rng.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 |
11 | #include
12 |
13 | namespace RNG {
14 | using engine_type = trng::yarn2;
15 | engine_type engine_distributed;
16 | engine_type engine_shared;
17 |
18 | void init(unsigned long seed) {
19 | int rank, size;
20 | MPI_Comm_rank(MPI_COMM_WORLD, &rank);
21 | MPI_Comm_size(MPI_COMM_WORLD, &size);
22 | engine_shared = engine_type();
23 | engine_distributed = engine_type();
24 |
25 | engine_distributed.seed(seed);
26 | engine_shared.seed(seed);
27 |
28 | engine_shared.split(2, 0);
29 |
30 | engine_distributed.split(2, 1);
31 | engine_distributed.split(size, rank);
32 | }
33 |
34 | void init(std::pair state) {
35 | std::stringstream shared_stream;
36 | std::stringstream distributed_stream;
37 |
38 | shared_stream.str(state.first);
39 | distributed_stream.str(state.second);
40 |
41 | shared_stream >> engine_shared;
42 | distributed_stream >> engine_distributed;
43 | }
44 |
45 | std::pair dump_state() {
46 | std::stringstream shared_stream;
47 | std::stringstream distributed_stream;
48 |
49 | shared_stream << engine_shared;
50 | distributed_stream << engine_distributed;
51 | return std::make_pair(shared_stream.str(), distributed_stream.str());
52 | }
53 |
54 | double uniform(double low, double high) { return trng::uniform_dist(low, high)(engine_distributed); }
55 | int uniform_int(int low, int high) { return trng::uniform_int_dist(low, high)(engine_distributed); }
56 | double normal(double mu, double sigma) { return trng::normal_dist(mu, sigma)(engine_distributed); }
57 | int poisson_int(double mu) { return trng::poisson_dist(mu)(engine_distributed); }
58 |
59 | double uniform_unsplit(double low, double high) { return trng::uniform_dist(low, high)(engine_shared); }
60 | int uniform_int_unsplit(int low, int high) { return trng::uniform_int_dist(low, high)(engine_shared); }
61 | double normal_unsplit(double mu, double sigma) { return trng::normal_dist(mu, sigma)(engine_shared); }
62 | int poisson_int_unsplit(double mu) { return trng::poisson_dist(mu)(engine_shared); }
63 | } // namespace RNG
64 |
--------------------------------------------------------------------------------
/src/skelly_sim.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 |
12 | int main(int argc, char *argv[]) {
13 | int thread_level;
14 | MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &thread_level);
15 |
16 | {
17 | const char *testargv[] = {"--kokkos-disable-warnings"};
18 | int testargc = 1;
19 | Kokkos::initialize(testargc, const_cast(testargv));
20 | }
21 |
22 | std::string config_file = "skelly_config.toml";
23 | bool resume_flag = false;
24 | bool overwrite_flag = false;
25 | bool listen_flag = false;
26 | Teuchos::CommandLineProcessor cmdp(false, true);
27 | cmdp.setOption("config-file", &config_file, "TOML input file.");
28 | cmdp.setOption("resume", "no-resume", &resume_flag, "Supply to resume simulation.");
29 | cmdp.setOption("overwrite", "no-overwrite", &overwrite_flag, "Supply to overwrite existing simulation.");
30 | cmdp.setOption("listen", "no-listen", &listen_flag, "Supply to run in listener mode");
31 |
32 | if (cmdp.parse(argc, argv) != Teuchos::CommandLineProcessor::PARSE_SUCCESSFUL) {
33 | MPI_Finalize();
34 | return EXIT_FAILURE;
35 | }
36 |
37 | try {
38 | if (resume_flag && overwrite_flag)
39 | throw std::runtime_error("Can't resume and overwrite simultaneously.");
40 | namespace fs = std::filesystem;
41 | if (listen_flag) {
42 | if (!fs::exists(fs::path{"skelly_sim.out"}))
43 | throw std::runtime_error("No trajectory detected for listener.");
44 | } else if (resume_flag) {
45 | if (!fs::exists(fs::path{"skelly_sim.out"}))
46 | throw std::runtime_error("--resume flag supplied without existing trajectory.");
47 | } else {
48 | if (!overwrite_flag && (fs::exists(fs::path{"skelly_sim.out"}) || fs::exists(fs::path{"skelly_sim.vf"})))
49 | throw std::runtime_error("Existing trajectory detected. Supply --overwrite flag to overwrite.");
50 | }
51 |
52 | System::init(config_file, resume_flag, listen_flag);
53 | if (listen_flag)
54 | listener::run();
55 | else
56 | System::run();
57 | } catch (const std::runtime_error &e) {
58 | // Warning: Critical only catches things on rank 0, so this may or may not print, if
59 | // some random rank throws an error. This is the same reason we use MPI_Abort: all
60 | // ranks are not guaranteed to land here, so there's only so much grace that can be
61 | // easily implemented.
62 | spdlog::critical(std::string("Fatal exception caught: ") + e.what());
63 | MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
64 | }
65 |
66 | MPI_Finalize();
67 | return EXIT_SUCCESS;
68 | }
69 |
--------------------------------------------------------------------------------
/src/skelly_sim/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/src/skelly_sim/__init__.py
--------------------------------------------------------------------------------
/src/skelly_sim/paraview_utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/src/skelly_sim/paraview_utils/__init__.py
--------------------------------------------------------------------------------
/src/skelly_sim/paraview_utils/body_reader.py:
--------------------------------------------------------------------------------
1 | import vtk
2 | from trajectory_utility import load_frame
3 | import toml
4 |
5 | outInfo = self.GetOutputInformation(0)
6 |
7 | if outInfo.Has(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP()):
8 | time = outInfo.Get(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP())
9 | else:
10 | time = 0
11 |
12 | timestep = len(self.times) - 1
13 | for i in range(len(self.times) - 1):
14 | if time < self.times[i+1] and time >= self.times[i]:
15 | timestep = i
16 |
17 | frame = load_frame(self.fhs, self.fpos, timestep)
18 |
19 | with open(toml_file) as f:
20 | skelly_config = toml.load(f)
21 |
22 | mb = vtk.vtkMultiBlockDataSet()
23 | offset = 0
24 | for i, body in enumerate(frame["bodies"]):
25 | position = body["position_"][3:]
26 | s = vtk.vtkSphereSource()
27 | s.SetRadius(skelly_config["bodies"][i]['radius'])
28 | s.SetCenter(position)
29 | s.SetThetaResolution(32)
30 | s.SetPhiResolution(32)
31 | s.Update()
32 | mb.SetBlock(offset, s.GetOutput())
33 | offset += 1
34 |
35 | self.GetOutput().ShallowCopy(mb)
36 |
--------------------------------------------------------------------------------
/src/skelly_sim/paraview_utils/body_reader_request.py:
--------------------------------------------------------------------------------
1 | import msgpack
2 | from pathlib import Path
3 | import vtk
4 | from trajectory_utility import get_frame_info
5 |
6 | outInfo = self.GetOutputInformation(0)
7 |
8 | print("Initializing bodies")
9 | self.fhs, self.fpos, self.times = get_frame_info(sorted(Path('.').glob('skelly_sim.out.*')))
10 | outInfo.Set(vtk.vtkStreamingDemandDrivenPipeline.TIME_RANGE(), [self.times[0], self.times[-1]], 2)
11 | outInfo.Set(vtk.vtkStreamingDemandDrivenPipeline.TIME_STEPS(), self.times, len(self.times))
12 |
--------------------------------------------------------------------------------
/src/skelly_sim/paraview_utils/fiber_reader.py:
--------------------------------------------------------------------------------
1 | import vtk
2 | from trajectory_utility import load_frame
3 |
4 | outInfo = self.GetOutputInformation(0)
5 |
6 | if outInfo.Has(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP()):
7 | time = outInfo.Get(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP())
8 | else:
9 | time = 0
10 |
11 | timestep = len(self.times) - 1
12 | for i in range(len(self.times) - 1):
13 | if time < self.times[i+1] and time >= self.times[i]:
14 | timestep = i
15 | break
16 |
17 | frame = load_frame(self.fhs, self.fpos, timestep)
18 |
19 | pts = vtk.vtkPoints()
20 | lines = vtk.vtkCellArray()
21 | offset = 0
22 | for fib in frame["fibers"]:
23 | n_nodes = fib["n_nodes_"]
24 | lines.InsertNextCell(n_nodes)
25 | pl = vtk.vtkPolyLine()
26 | pl.GetPointIds().SetNumberOfIds(n_nodes)
27 |
28 | for i in range(n_nodes):
29 | low = 3 + i * 3
30 | lines.InsertCellPoint(offset)
31 | pts.InsertPoint(offset, fib["x_"][low : low + 3])
32 | offset += 1
33 |
34 | pd = self.GetPolyDataOutput()
35 | pd.SetPoints(pts)
36 | pd.SetLines(lines)
37 |
--------------------------------------------------------------------------------
/src/skelly_sim/paraview_utils/fiber_reader_request.py:
--------------------------------------------------------------------------------
1 | import msgpack
2 | from pathlib import Path
3 | from trajectory_utility import get_frame_info
4 |
5 | outInfo = self.GetOutputInformation(0)
6 | print("Initializing fibers")
7 | self.fhs, self.fpos, self.times = get_frame_info(sorted(Path('.').glob('skelly_sim.out.*')))
8 | outInfo.Set(vtk.vtkStreamingDemandDrivenPipeline.TIME_RANGE(), [self.times[0], self.times[-1]], 2)
9 | outInfo.Set(vtk.vtkStreamingDemandDrivenPipeline.TIME_STEPS(), self.times, len(self.times))
10 |
--------------------------------------------------------------------------------
/src/skelly_sim/paraview_utils/field_reader.py:
--------------------------------------------------------------------------------
1 | import vtk
2 | from trajectory_utility import load_field_frame
3 | import numpy as np
4 |
5 | outInfo = self.GetOutputInformation(0)
6 |
7 | if outInfo.Has(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP()):
8 | time = outInfo.Get(vtk.vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP())
9 | else:
10 | time = 0
11 |
12 | timestep = len(self.times) - 1
13 | for i in range(len(self.times) - 1):
14 | if time < self.times[i+1] and time >= self.times[i]:
15 | timestep = i
16 |
17 | frame = load_field_frame(self.fhs, self.fpos, timestep)
18 |
19 | pts = vtk.vtkPoints()
20 | npts = int(sum([data["x_grid"][2] for data in frame]))
21 | vertices = vtk.vtkPolyData()
22 |
23 | velocities = vtk.vtkDoubleArray()
24 | velocities.SetName("velocities")
25 | velocities.SetNumberOfComponents(3)
26 | velocities.SetNumberOfTuples(npts)
27 |
28 | magnitudes = vtk.vtkDoubleArray()
29 | magnitudes.SetName("magnitudes")
30 | magnitudes.SetNumberOfComponents(0)
31 | magnitudes.SetNumberOfValues(npts)
32 |
33 | offset = 0
34 | for data in frame:
35 | n_points_local = data["x_grid"][2]
36 | x_grid = data["x_grid"][3:]
37 | v_grid = data["v_grid"][3:]
38 | for i in range(n_points_local):
39 | pts.InsertPoint(offset, x_grid[3 * i : 3* (i + 1)])
40 | # verts.InsertNextCell(1, [offset])
41 | mag = np.linalg.norm(np.array(v_grid[3*i:3*(i+1)]))
42 | magnitudes.SetValue(offset, mag)
43 | velocities.SetTuple(offset, v_grid[3 * i : 3* (i + 1)])
44 | offset += 1
45 |
46 | pd = self.GetPolyDataOutput()
47 | pd.SetPoints(pts)
48 | pd.GetPointData().AddArray(velocities)
49 | pd.GetPointData().AddArray(magnitudes)
50 |
--------------------------------------------------------------------------------
/src/skelly_sim/paraview_utils/field_reader_request.py:
--------------------------------------------------------------------------------
1 | import msgpack
2 | from pathlib import Path
3 | from trajectory_utility import get_frame_info
4 |
5 | outInfo = self.GetOutputInformation(0)
6 | print("Initializing velocity field")
7 | self.fhs, self.fpos, self.times = get_frame_info(sorted(Path('.').glob('skelly_sim.vf.*')))
8 | print(self.times)
9 | outInfo.Set(vtk.vtkStreamingDemandDrivenPipeline.TIME_RANGE(), [self.times[0], self.times[-1]], 2)
10 | outInfo.Set(vtk.vtkStreamingDemandDrivenPipeline.TIME_STEPS(), self.times, len(self.times))
11 | print("Finished init")
12 |
--------------------------------------------------------------------------------
/src/skelly_sim/paraview_utils/trajectory_utility.py:
--------------------------------------------------------------------------------
1 | import msgpack
2 |
3 |
4 | class DesyncError(Exception):
5 | pass
6 |
7 |
8 | def load_frame(fhs, fpos, index):
9 | data = []
10 | for i in range(len(fhs)):
11 | fhs[i].seek(fpos[i][index])
12 | data.append(msgpack.Unpacker(fhs[i], raw=False).unpack())
13 |
14 | time = data[0]["time"]
15 | dt = data[0]["dt"]
16 | fibers = []
17 | bodies = []
18 | for el in data:
19 | if el["time"] != time or el["dt"] != dt:
20 | raise DesyncError
21 | fibers.extend(el["fibers"][0])
22 | el.pop("fibers")
23 |
24 | data[0]["fibers"] = fibers
25 | data[0]["bodies"] = data[0]["bodies"][0]
26 |
27 | return data[0]
28 |
29 |
30 | def load_field_frame(fhs, fpos, index):
31 | data = []
32 | for i in range(len(fhs)):
33 | fhs[i].seek(fpos[i][index])
34 | data.append(msgpack.Unpacker(fhs[i], raw=False).unpack())
35 |
36 | return data
37 |
38 |
39 | def get_frame_info(filenames):
40 | if not filenames:
41 | return [], [], []
42 |
43 | unpackers = []
44 | fhs = []
45 | times = []
46 | for filename in filenames:
47 | f = open(filename, "rb")
48 | fhs.append(f)
49 | unpackers.append(msgpack.Unpacker(f, raw=False))
50 |
51 | fpos = [[] for i in range(len(filenames))]
52 | while True:
53 | try:
54 | for i in range(len(unpackers)):
55 | fpos[i].append(unpackers[i].tell())
56 | if i == 0:
57 | n_keys = unpackers[i].read_map_header()
58 | for ikey in range(n_keys):
59 | key = unpackers[i].unpack()
60 | if key == 'time':
61 | times.append(unpackers[i].unpack())
62 | else:
63 | unpackers[i].skip()
64 | else:
65 | unpackers[i].skip()
66 |
67 | except msgpack.exceptions.OutOfData:
68 | fpos[0].pop()
69 | break
70 |
71 | return fhs, fpos, times
72 |
--------------------------------------------------------------------------------
/src/skelly_sim/periphery.py:
--------------------------------------------------------------------------------
1 | """
2 | Small class to handle the periphery.
3 | """
4 | import numpy as np
5 | import skelly_sim.kernels as kernels
6 |
7 |
8 | class Periphery(object):
9 | """
10 | Small class to handle a single body.
11 | """
12 | def __init__(self, location, orientation, reference_configuration, reference_normals, quadrature_weights):
13 | """
14 | Constructor. Take arguments like ...
15 | """
16 | # Location as np.array.shape = 3
17 | self.location = location
18 | # Orientation as Quaternion
19 | self.orientation = orientation
20 | # Number of blobs
21 | self.Nblobs = reference_configuration.size // 3
22 | # Reference configuration. Coordinates of blobs for quaternion [1, 0, 0, 0]
23 | # and location = np.array[0, 0, 0]) as a np.array.shape = (Nblobs, 3)
24 | # or np.array.shape = (Nblobs * 3)
25 | self.reference_configuration = np.reshape(reference_configuration, (self.Nblobs, 3))
26 | self.reference_normals = np.reshape(reference_normals, (self.Nblobs, 3))
27 | self.quadrature_weights = quadrature_weights.flatten()
28 | self.Nblobs = self.quadrature_weights.size
29 | # Name of body and type of body. A string or number
30 | self.name = None
31 | self.type = None
32 | self.rotation_matrix = None
33 | # Some default functions
34 | self.function_slip = np.zeros((self.Nblobs, 3))
35 | self.ID = None
36 | # Vectors for singularity subtractions
37 | self.ex = None
38 | self.ey = None
39 | self.ez = None
40 | self.density = np.zeros(3 * self.Nblobs)
41 | self.density_new = np.zeros(3 * self.Nblobs)
42 |
43 | def get_r_vectors(self, location=None, orientation=None):
44 | """
45 | Return the coordinates of the blobs.
46 | """
47 | # Get location and orientation
48 | if location is None:
49 | location = self.location
50 | if orientation is None:
51 | orientation = self.orientation
52 |
53 | # Compute blobs coordinates
54 | rotation_matrix = orientation.rotation_matrix()
55 | r_vectors = np.array([np.dot(rotation_matrix, vec) for vec in self.reference_configuration])
56 | r_vectors += location
57 | return r_vectors
58 |
59 | def get_normals(self, orientation=None):
60 | """
61 | Return the normals of the periphery.
62 | """
63 | # Get orientation
64 | if orientation is None:
65 | orientation = self.orientation
66 |
67 | # Compute blobs coordinates
68 | rotation_matrix = orientation.rotation_matrix()
69 | normals = np.array([np.dot(rotation_matrix, vec) for vec in self.reference_normals])
70 | return normals
71 |
72 | def get_singularity_subtraction_vectors(self, eta=1):
73 |
74 | # Compute correction for singularity subtractions
75 | r_vectors_blobs = self.get_r_vectors()
76 | normals = self.get_normals()
77 | quadrature_weights = self.quadrature_weights
78 | Nperiphery = quadrature_weights.size
79 | e = np.zeros((Nperiphery, 3))
80 | e[:, 0] = 1.0
81 | e *= quadrature_weights[:, None]
82 | self.ex = kernels.stresslet_kernel_times_normal_times_density_numba(r_vectors_blobs, normals, e, eta)
83 | e[:, :] = 0.0
84 | e[:, 1] = 1.0
85 | e *= quadrature_weights[:, None]
86 | self.ey = kernels.stresslet_kernel_times_normal_times_density_numba(r_vectors_blobs, normals, e, eta)
87 | e[:, :] = 0.0
88 | e[:, 2] = 1.0
89 | e *= quadrature_weights[:, None]
90 | self.ez = kernels.stresslet_kernel_times_normal_times_density_numba(r_vectors_blobs, normals, e, eta)
91 |
92 | return
93 |
--------------------------------------------------------------------------------
/src/skelly_sim/quaternion.py:
--------------------------------------------------------------------------------
1 | '''
2 | Simple quaternion object for use with quaternion integrators.
3 | '''
4 | import numpy as np
5 |
6 |
7 | class Quaternion(object):
8 | def __init__(self, entries):
9 | ''' Constructor, takes 4 entries = s, p1, p2, p3 as a numpy array. '''
10 | self.entries = entries
11 | self.s = np.array(entries[0])
12 | self.p = np.array(entries[1:4])
13 |
14 | @classmethod
15 | def from_rotation(cls, phi):
16 | ''' Create a quaternion given an angle of rotation phi,
17 | which represents a rotation clockwise about the vector phi of magnitude
18 | phi. This will be used with phi = omega*dt or similar in the integrator.'''
19 | phi_norm = np.linalg.norm(phi)
20 | s = np.array([np.cos(phi_norm / 2.)])
21 | if phi_norm != 0:
22 | p = np.sin(phi_norm / 2) * (phi / phi_norm)
23 | else:
24 | p = np.zeros(3)
25 | return cls(np.concatenate([s, p]))
26 |
27 | def __mul__(self, other):
28 | '''
29 | Quaternion multiplication. In this case, other is the
30 | right quaternion.
31 | '''
32 | s = (self.s * other.s - np.dot(self.p, other.p))
33 | p = (self.s * other.p + other.s * self.p + np.cross(self.p, other.p))
34 | return Quaternion(np.concatenate(([s], p)))
35 |
36 | def rotation_matrix(self):
37 | '''
38 | Return the rotation matrix representing rotation
39 | by this quaternion.
40 | '''
41 | # Cross product matrix for p, actually the negative.
42 | diag = self.s**2 - 0.5
43 | return 2.0 * np.array([[
44 | self.p[0]**2 + diag, self.p[0] * self.p[1] - self.s * self.p[2], self.p[0] * self.p[2] + self.s * self.p[1]
45 | ], [
46 | self.p[1] * self.p[0] + self.s * self.p[2], self.p[1]**2 + diag, self.p[1] * self.p[2] - self.s * self.p[0]
47 | ], [
48 | self.p[2] * self.p[0] - self.s * self.p[1], self.p[2] * self.p[1] + self.s * self.p[0], self.p[2]**2 + diag
49 | ]])
50 |
51 | def __str__(self):
52 | return '[ %f, %f, %f, %f ]' % (self.s, self.p[0], self.p[1], self.p[2])
53 |
54 | def inverse(self):
55 | ''' Return the inverse quaternion.'''
56 | return Quaternion([self.s, -1. * self.p[0], -1. * self.p[1], -1. * self.p[2]])
57 |
58 | def rotation_angle(self):
59 | ''' Return 3 dimensional rotation angle that the quaternion represents. '''
60 | phi_norm = 2. * np.arccos(self.s)
61 | return phi_norm * self.p / (np.linalg.norm(self.p))
62 |
63 | def random_orientation(self):
64 | '''Give this quaternion object a random orientation'''
65 | theta = np.random.normal(0., 1., 4)
66 | theta = theta / np.linalg.norm(theta)
67 | self.entries = theta
68 | self.s = theta[0]
69 | self.p = theta[1:4]
70 |
--------------------------------------------------------------------------------
/src/skelly_sim/testing.py:
--------------------------------------------------------------------------------
1 | import contextlib
2 | import os
3 | import pytest
4 |
5 | from pathlib import Path
6 | from subprocess import run
7 |
8 | @contextlib.contextmanager
9 | def working_directory(path):
10 | """Changes working directory and returns to previous on exit."""
11 | prev_cwd = Path.cwd()
12 | os.chdir(path)
13 | try:
14 | yield
15 | finally:
16 | os.chdir(prev_cwd)
17 |
18 | @pytest.fixture(scope="session")
19 | def sim_path(tmp_path_factory):
20 | path = tmp_path_factory.mktemp("sims")
21 | return path
22 |
23 | def run_sim(path: Path=Path('.')):
24 | print("Running simulation")
25 | with working_directory(path):
26 | res = run(['skelly_sim', '--overwrite'], shell=False, capture_output=False)
27 | return not res.returncode
28 |
29 | def run_precompute(path: Path=Path('.')):
30 | print("Generating precompute data")
31 | with working_directory(path):
32 | res = run(['skelly_precompute'], shell=False, capture_output=True)
33 | return not res.returncode
34 |
--------------------------------------------------------------------------------
/tests/combined/bodies/test_compare_quadrature.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | from pathlib import Path
4 | from skelly_sim.reader import TrajectoryReader
5 | from skelly_sim.skelly_config import Config, Body
6 | from skelly_sim.testing import working_directory, run_sim, run_precompute, sim_path
7 |
8 | np.random.seed(100)
9 | n_nodes = 800
10 | config_file = 'skelly_config.toml'
11 |
12 | def gen_config(path: Path=('.')):
13 | print("Generating config")
14 | # create a config object and set the system parameters
15 | config = Config()
16 | config.params.eta = 0.9
17 | config.params.dt_initial = 1E-1
18 | config.params.dt_min = 1E-4
19 | config.params.dt_max = 1E-1
20 | config.params.dt_write = 1E-1
21 | config.params.t_final = 0.2
22 | config.params.gmres_tol = 1E-10
23 | config.params.seed = 130319
24 | config.params.pair_evaluator = "CPU"
25 |
26 | config.bodies = [
27 | Body(n_nucleation_sites=0,
28 | position=[0.0, 0.0, 0.0],
29 | shape='ellipsoid',
30 | axis_length=[0.5, 0.5, 0.5],
31 | n_nodes=n_nodes,
32 | external_force=[0.0, 0.0, 1.5],
33 | precompute_file='ellipsoidal_precompute.npz'),
34 | Body(n_nucleation_sites=0,
35 | position=[5.0, 0.0, 0.0],
36 | shape='sphere',
37 | radius=0.5,
38 | n_nodes=n_nodes,
39 | external_force=[0.0, 0.0, 1.5],
40 | precompute_file='sphere_precompute.npz'),
41 | ]
42 |
43 | config.save(path / config_file)
44 | return True
45 |
46 | def compare_quadrature(path: Path=Path('.')):
47 | print("Comparing quadrature between a sphere and a spherical ellipsoid (also sphere)")
48 | with working_directory(path):
49 | # Get the precompute data to compare the quadrature, etc
50 | precompute_ellipsoid = np.load("ellipsoidal_precompute.npz")
51 | precompute_sphere = np.load("sphere_precompute.npz")
52 |
53 | # Compare node weights
54 | weights_compare = np.allclose(precompute_ellipsoid['node_weights'], precompute_sphere['node_weights'], atol=1e-12)
55 |
56 | # Compare node normals
57 | normals_compare = np.allclose(precompute_ellipsoid['node_normals_ref'], precompute_sphere['node_normals_ref'], atol=1e-12)
58 |
59 | # Compare node positions
60 | positions_compare = np.allclose(precompute_ellipsoid['node_positions_ref'], precompute_sphere['node_positions_ref'], atol=1e-12)
61 |
62 | return (weights_compare and normals_compare and positions_compare)
63 |
64 | def test_gen_config(sim_path):
65 | assert gen_config(sim_path)
66 |
67 | def test_run_precompute(sim_path):
68 | assert run_precompute(sim_path)
69 |
70 | def test_quadrature(sim_path):
71 | assert compare_quadrature(sim_path)
72 |
73 |
--------------------------------------------------------------------------------
/tests/combined/bodies/test_ellipsoid_assphere_constforce.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | from pathlib import Path
4 | from skelly_sim.reader import TrajectoryReader
5 | from skelly_sim.skelly_config import Config, Body
6 | from skelly_sim.testing import working_directory, run_sim, run_precompute, sim_path
7 |
8 | np.random.seed(100)
9 | #n_nodes = 2000
10 | n_nodes = 800
11 | config_file = 'skelly_config.toml'
12 |
13 | def gen_config(path: Path=('.')):
14 | print("Generating config")
15 | # create a config object and set the system parameters
16 | config = Config()
17 | config.params.eta = 0.9
18 | config.params.dt_initial = 1E-1
19 | config.params.dt_min = 1E-4
20 | config.params.dt_max = 1E-1
21 | config.params.dt_write = 1E-1
22 | config.params.t_final = 1.0
23 | config.params.gmres_tol = 1E-10
24 | config.params.seed = 130319
25 | config.params.pair_evaluator = "CPU"
26 |
27 | config.bodies = [
28 | Body(n_nucleation_sites=0,
29 | position=[0.0, 0.0, 0.0],
30 | shape='ellipsoid',
31 | axis_length=[0.5, 0.5, 0.5],
32 | n_nodes=n_nodes,
33 | external_force=[0.0, 0.0, 1.5])
34 | ]
35 |
36 | config.save(path / config_file)
37 | return True
38 |
39 | def velocity(path: Path=Path('.')):
40 | print("Running analysis")
41 | with working_directory(path):
42 | traj = TrajectoryReader(config_file)
43 |
44 | # need beginning/end positions to calculate average velocity
45 | traj.load_frame(0)
46 | z_initial = traj['bodies'][0]['position_'][2]
47 | v_initial = traj['bodies'][0]['solution_vec_'][-4]
48 | traj.load_frame(len(traj) - 1)
49 | z_final = traj['bodies'][0]['position_'][2]
50 | v_final = traj['bodies'][0]['solution_vec_'][-4]
51 |
52 | dt = traj.times[-1] - traj.times[0]
53 |
54 | # We need the hydrodynamic radius, which is slightly different than the supplied 'attachment'
55 | # radius. let's just grab it from the precompute data.
56 | precompute_data = np.load(traj.config_data['bodies'][0]['precompute_file'])
57 |
58 | # Find the radius from all of the node_positions_ref, and look for the max (should be around 0.4)
59 | radii = np.zeros(len(precompute_data['node_positions_ref']))
60 | for idx in range(len(radii)):
61 | radii[idx] = np.linalg.norm(precompute_data['node_positions_ref'][idx,:])
62 | radius = np.mean(radii)
63 | print("Effective radius XY: {}".format(radius))
64 | eta = traj.config_data['params']['eta']
65 | force = traj.config_data['bodies'][0]['external_force'][2]
66 |
67 | v_theoretical = force / (6 * np.pi * eta * radius)
68 | v_measured = (z_final - z_initial) / dt
69 | error = abs(1 - v_measured / v_theoretical)
70 |
71 | print("Measured velocity: {}".format(v_measured))
72 | print("v_initial: {}".format(v_initial))
73 | print("v_final: {}".format(v_final))
74 | print("Theoretical velocity: {}".format(v_theoretical))
75 | print("Error |1 - v/v0|: {}".format(error))
76 |
77 | return error
78 |
79 |
80 | def test_gen_config(sim_path):
81 | assert gen_config(sim_path)
82 |
83 | def test_run_precompute(sim_path):
84 | assert run_precompute(sim_path)
85 |
86 | def test_run_sim(sim_path):
87 | assert run_sim(sim_path)
88 |
89 | def test_velocity(sim_path):
90 | assert velocity(sim_path) < 1E-6
91 |
--------------------------------------------------------------------------------
/tests/combined/bodies/test_ellipsoid_prolate_z_torque.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | from pathlib import Path
4 | from skelly_sim.reader import TrajectoryReader
5 | from skelly_sim.skelly_config import Config, Body
6 | from skelly_sim.testing import working_directory, run_sim, run_precompute, sim_path
7 |
8 | np.random.seed(100)
9 | n_nodes = 1000
10 | config_file = 'skelly_config.toml'
11 |
12 | # This test is based off of Kim and Karilla, pg. 64 equations for a prolate spheriod under torque
13 | # Note that the torque is about the main symmetry axis (rotating in this case around z)
14 |
15 | def gen_config(path: Path=('.')):
16 | print("Generating config")
17 | # create a config object and set the system parameters
18 | config = Config()
19 | config.params.eta = 0.9
20 | config.params.dt_initial = 1E-1
21 | config.params.dt_min = 1E-4
22 | config.params.dt_max = 1E-1
23 | config.params.dt_write = 1E-1
24 | config.params.t_final = 1.5
25 | config.params.gmres_tol = 1E-10
26 | config.params.seed = 130319
27 | config.params.pair_evaluator = "CPU"
28 |
29 | # Generate an axis length for a,b,c to get a 3:1 ratio, accounting for the shifts in radii
30 | body_a = 1.1
31 | body_b = 1.1
32 | body_c = 3.2
33 |
34 | config.bodies = [
35 | Body(n_nucleation_sites=0,
36 | position=[0.0, 0.0, 0.0],
37 | shape='ellipsoid',
38 | axis_length=[body_a, body_b, body_c],
39 | n_nodes=n_nodes,
40 | external_torque=[0.0, 0.0, 0.6])
41 | ]
42 |
43 | config.save(path / config_file)
44 | return True
45 |
46 | def angular_velocity(path: Path=Path('.')):
47 | print("Running analysis")
48 | with working_directory(path):
49 | traj = TrajectoryReader(config_file)
50 |
51 | # need beginning/end positions to calculate average velocity
52 | traj.load_frame(0)
53 | omega_initial = traj['bodies'][0]['solution_vec_'][-3:]
54 | traj.load_frame(len(traj) - 1)
55 | omega_final = traj['bodies'][0]['solution_vec_'][-3:]
56 |
57 | # Use the same radii as above, minus the correction factors
58 | a = 3.0
59 | c = 1.0
60 | eccentricity = np.sqrt(a**2 - c**2)/a
61 | print("Eccentricity: {}".format(eccentricity))
62 | eta = traj.config_data['params']['eta']
63 | torque = np.linalg.norm(traj.config_data['bodies'][0]['external_torque'])
64 |
65 | # Calculate
66 | oneme2 = (1.0 - np.power(eccentricity,2))
67 | Le = np.log((1.0+eccentricity)/(1.0-eccentricity))
68 | Xc = (4./3.)*np.power(eccentricity,3)*(oneme2) / (2*eccentricity - oneme2*Le)
69 |
70 | w_theoretical = torque / (8. * np.pi * eta * a**3 * Xc)
71 | w_initial = omega_initial[2]
72 |
73 | error = abs(1 - w_initial / w_theoretical)
74 |
75 | print("Measured angular velocity: {}".format(w_initial))
76 | print("Theoretical angular velocity {}".format(w_theoretical))
77 | print("Error |1 - w/w0|: {}".format(error))
78 |
79 | return error
80 |
81 |
82 | def test_gen_config(sim_path):
83 | assert gen_config(sim_path)
84 |
85 | def test_run_precompute(sim_path):
86 | assert run_precompute(sim_path)
87 |
88 | def test_run_sim(sim_path):
89 | assert run_sim(sim_path)
90 |
91 | def test_angular_velocity(sim_path):
92 | assert angular_velocity(sim_path) < 1E-5
93 |
--------------------------------------------------------------------------------
/tests/combined/regression_tests/fdfiber_compression_data.npy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/tests/combined/regression_tests/fdfiber_compression_data.npy
--------------------------------------------------------------------------------
/tests/combined/regression_tests/fdfiber_compression_finalpositions.npz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/tests/combined/regression_tests/fdfiber_compression_finalpositions.npz
--------------------------------------------------------------------------------
/tests/combined/test_body_const_force.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | from pathlib import Path
4 | from skelly_sim.reader import TrajectoryReader
5 | from skelly_sim.skelly_config import Config, Body
6 | from skelly_sim.testing import working_directory, run_sim, run_precompute, sim_path
7 |
8 | np.random.seed(100)
9 | n_nodes = 800
10 | config_file = 'skelly_config.toml'
11 |
12 | def gen_config(path: Path=('.')):
13 | print("Generating config")
14 | # create a config object and set the system parameters
15 | config = Config()
16 | config.params.eta = 0.9
17 | config.params.dt_initial = 1E-1
18 | config.params.dt_min = 1E-4
19 | config.params.dt_max = 1E-1
20 | config.params.dt_write = 1E-1
21 | config.params.t_final = 1.0
22 | config.params.gmres_tol = 1E-10
23 | config.params.seed = 130319
24 | config.params.pair_evaluator = "CPU"
25 |
26 | config.bodies = [
27 | Body(n_nucleation_sites=0,
28 | position=[0.0, 0.0, 0.0],
29 | shape='sphere',
30 | radius=0.5,
31 | n_nodes=n_nodes,
32 | external_force=[0.0, 0.0, 1.5])
33 | ]
34 |
35 | config.save(path / config_file)
36 | return True
37 |
38 | def velocity(path: Path=Path('.')):
39 | print("Running analysis")
40 | with working_directory(path):
41 | traj = TrajectoryReader(config_file)
42 |
43 | # need beginning/end positions to calculate average velocity
44 | traj.load_frame(0)
45 | z_initial = traj['bodies'][0]['position_'][2]
46 | traj.load_frame(len(traj) - 1)
47 | z_final = traj['bodies'][0]['position_'][2]
48 |
49 | dt = traj.times[-1] - traj.times[0]
50 |
51 | # We need the hydrodynamic radius, which is slightly different than the supplied 'attachment'
52 | # radius. let's just grab it from the precompute data.
53 | precompute_data = np.load(traj.config_data['bodies'][0]['precompute_file'])
54 | radius = np.linalg.norm(precompute_data["node_positions_ref"][0])
55 | eta = traj.config_data['params']['eta']
56 | force = traj.config_data['bodies'][0]['external_force'][2]
57 |
58 | print("Effective radius: {}".format(radius))
59 |
60 | v_theoretical = force / (6 * np.pi * eta * radius)
61 | v_measured = (z_final - z_initial) / dt
62 | error = abs(1 - v_measured / v_theoretical)
63 |
64 | print("Measured velocity: {}".format(v_measured))
65 | print("Theoretical velocity: {}".format(v_theoretical))
66 | print("Error |1 - v/v0|: {}".format(error))
67 |
68 | return error
69 |
70 |
71 | def test_gen_config(sim_path):
72 | assert gen_config(sim_path)
73 |
74 | def test_run_precompute(sim_path):
75 | assert run_precompute(sim_path)
76 |
77 | def test_run_sim(sim_path):
78 | assert run_sim(sim_path)
79 |
80 | def test_velocity(sim_path):
81 | assert velocity(sim_path) < 1E-6
82 |
--------------------------------------------------------------------------------
/tests/combined/test_body_const_torque.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | from pathlib import Path
4 | from skelly_sim.reader import TrajectoryReader
5 | from skelly_sim.skelly_config import Config, Body
6 | from skelly_sim.testing import working_directory, run_sim, run_precompute, sim_path
7 |
8 | np.random.seed(100)
9 | n_nodes = 800
10 | config_file = 'skelly_config.toml'
11 |
12 | def gen_config(path):
13 | print("Generating config")
14 | # create a config object and set the system parameters
15 | config = Config()
16 | config.params.eta = 0.9
17 | config.params.dt_initial = 1E-1
18 | config.params.dt_min = 1E-4
19 | config.params.dt_max = 1E-1
20 | config.params.dt_write = 1E-1
21 | config.params.t_final = 1.0
22 | config.params.gmres_tol = 1E-10
23 | config.params.seed = 130319
24 | config.params.pair_evaluator = "CPU"
25 |
26 | config.bodies = [
27 | Body(n_nucleation_sites=0,
28 | position=[0.0, 0.0, 0.0],
29 | shape='sphere',
30 | radius=0.5,
31 | n_nodes=n_nodes,
32 | external_force=[0.0, 0.0, 0.0],
33 | external_torque=[0.0, 0.0, 1.2]
34 | )
35 | ]
36 |
37 | config.save(path / config_file)
38 | return True
39 |
40 | def angular_velocity(path: Path=Path('.')):
41 | print("Running analysis")
42 | with working_directory(path):
43 | traj = TrajectoryReader(config_file)
44 |
45 | # need beginning/end positions to calculate average velocity
46 | traj.load_frame(0)
47 | Theta_initial = traj['bodies'][0]['orientation_']
48 | traj.load_frame(len(traj) - 1)
49 | Theta_final = traj['bodies'][0]['orientation_']
50 |
51 | precompute_data = np.load(traj.config_data['bodies'][0]['precompute_file'])
52 | radius = np.linalg.norm(precompute_data["node_positions_ref"][0])
53 | eta = traj.config_data['params']['eta']
54 | torque = np.linalg.norm(traj.config_data['bodies'][0]['external_torque'])
55 | dt = traj.times[-1] - traj.times[0]
56 |
57 | w_theoretical = torque / (8 * np.pi * eta * radius**3)
58 | w_measured = 2 * np.arccos(np.dot(Theta_initial, Theta_final)) / dt
59 |
60 | return np.abs(1 - w_measured / w_theoretical)
61 |
62 |
63 | def test_gen_config(sim_path):
64 | assert gen_config(sim_path)
65 |
66 | def test_run_precompute(sim_path):
67 | assert run_precompute(sim_path)
68 |
69 | def test_run_sim(sim_path):
70 | assert run_sim(sim_path)
71 |
72 | def test_angular_velocity(sim_path):
73 | assert angular_velocity(sim_path) < 1E-6
74 |
--------------------------------------------------------------------------------
/tests/combined/test_body_oscillatory_force.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | from pathlib import Path
4 | from skelly_sim.reader import TrajectoryReader
5 | from skelly_sim.skelly_config import ConfigSpherical, Body
6 | from skelly_sim.testing import working_directory, run_sim, run_precompute, sim_path
7 |
8 | np.random.seed(100)
9 | config_file = 'skelly_config.toml'
10 |
11 | def gen_config(path: Path=('.')):
12 | print("Generating config")
13 | # create a config object and set the system parameters
14 | config = ConfigSpherical()
15 | config.params.dt_initial = 5E-2
16 | config.params.dt_min = 1E-4
17 | config.params.dt_max = 5E-2
18 | config.params.dt_write = 5E-2
19 | config.params.t_final = 10.0
20 | config.params.gmres_tol = 1E-10
21 | config.params.seed = 130319
22 | config.params.pair_evaluator = "CPU"
23 |
24 | config.bodies = [
25 | Body(n_nucleation_sites=0,
26 | position=[0.0, 0.0, 0.0],
27 | shape='sphere',
28 | radius=0.5,
29 | n_nodes=400,
30 | external_force_type='Oscillatory',
31 | external_force=[0.0, 0.0, 1.0],
32 | external_oscillation_force_amplitude=2.0,
33 | external_oscillation_force_frequency=0.1,
34 | external_oscillation_force_phase=0.0)
35 | ]
36 |
37 | config.periphery.n_nodes = 500
38 | config.periphery.radius = 4.0/1.04
39 |
40 | config.save(path / config_file)
41 | return True
42 |
43 | def z_final_position(path: Path=Path('.')):
44 | print("Running analysis")
45 | with working_directory(path):
46 | traj = TrajectoryReader(config_file)
47 |
48 | # Get start/end positions
49 | traj.load_frame(0)
50 | z_initial = traj['bodies'][0]['position_'][2]
51 | traj.load_frame(len(traj) - 1)
52 | z_final = traj['bodies'][0]['position_'][2]
53 |
54 | # Since this is an oscillatory force, check where the sphere returned to
55 | z_final_theoretical = 6.215481243240294E-05
56 | error = abs(1 - z_final / z_final_theoretical)
57 | print(f"Measured final position: {z_final}")
58 | print(f"Theoretical final position: {z_final_theoretical}")
59 | print(f"Error: {z_final}")
60 |
61 | return error
62 |
63 |
64 | def test_gen_config(sim_path):
65 | assert gen_config(sim_path)
66 |
67 | def test_run_precompute(sim_path):
68 | assert run_precompute(sim_path)
69 |
70 | def test_run_sim(sim_path):
71 | assert run_sim(sim_path)
72 |
73 | def test_velocity(sim_path):
74 | assert z_final_position(sim_path) < 1E-6
75 |
--------------------------------------------------------------------------------
/tests/combined/test_clamped_buckling_sigma72.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy.signal import find_peaks
3 |
4 | from pathlib import Path
5 |
6 | from skelly_sim.reader import TrajectoryReader
7 | from skelly_sim.skelly_config import Config, Fiber, Point
8 | from skelly_sim.testing import working_directory, sim_path, run_sim
9 |
10 | np.random.seed(100)
11 | config_file = 'skelly_config.toml'
12 |
13 | def gen_config(path: Path):
14 | print("Generating config")
15 | # create a config object and set the system parameters
16 | config = Config()
17 | config.params.eta = 1.0
18 | config.params.dt_initial = 0.02
19 | config.params.dt_min = 0.01
20 | config.params.dt_max = 0.1
21 | config.params.dt_write = 0.1
22 | config.params.t_final = 50.0
23 | config.params.gmres_tol = 1E-10
24 | config.params.seed = 130319
25 | config.params.pair_evaluator = "CPU"
26 | config.params.adaptive_timestep_flag = True
27 |
28 | sigma = 72.0
29 | length = 1.0
30 | bending_rigidity = 0.0025
31 | force_scale = -sigma * bending_rigidity / length**3
32 | n_nodes = 32
33 |
34 | config.fibers = [Fiber(
35 | force_scale=force_scale,
36 | length=length,
37 | n_nodes=n_nodes,
38 | bending_rigidity=bending_rigidity,
39 | minus_clamped=True
40 | )]
41 |
42 | # Give a small starting kick
43 | config.point_sources = [
44 | Point(
45 | position=[0.0, 0.0, 10*length],
46 | force=[10.0, 0.0, 0.0],
47 | time_to_live=1.0,
48 | )
49 | ]
50 |
51 | # Orient in Z
52 | config.fibers[0].x = np.linspace([0, 0, 0], [0, 0, length], n_nodes).ravel().tolist()
53 |
54 | config.save(path / config_file)
55 |
56 | return True
57 |
58 | def is_decaying(path: Path=Path('.')):
59 | with working_directory(path):
60 | traj = TrajectoryReader('skelly_config.toml')
61 |
62 | x = []
63 | for i in range(len(traj)):
64 | traj.load_frame(i)
65 | x.append(traj['fibers'][0]['x_'][-1, 0])
66 |
67 | x = np.array(x)
68 | mpeaks, _ = find_peaks(x, height=0)
69 |
70 | # Check the peaks, ignoring the first one from the original 'kick' to the system
71 | x_peak1 = x[mpeaks[1]]
72 | x_peak2 = x[mpeaks[2]]
73 | print(f"peak 1 x: {x_peak1}")
74 | print(f"peak 2 x: {x_peak2}")
75 |
76 | # Just return if the solution is decaying at this timescale
77 | return x_peak2 < x_peak1
78 |
79 | def compare_previous_peaks(path: Path=Path('.')):
80 | print(f"Comparing deflection to previous results")
81 | with working_directory(path):
82 | traj = TrajectoryReader('skelly_config.toml')
83 |
84 | x = []
85 | for i in range(len(traj)):
86 | traj.load_frame(i)
87 | x.append(traj['fibers'][0]['x_'][-1, 0])
88 |
89 | x = np.array(x)
90 | mpeaks, _ = find_peaks(x, height=0)
91 |
92 | # Check the peaks, ignoring the first one from the original 'kick' to the system, compared to previous results
93 | x_peak1 = x[mpeaks[1]]
94 | x_peak2 = x[mpeaks[2]]
95 |
96 | x_peak1_test = 0.08844356
97 | x_peak2_test = 0.05563314
98 |
99 | rel_error_1 = abs(1 - x_peak1/x_peak1_test)
100 | rel_error_2 = abs(1 - x_peak2/x_peak2_test)
101 | rel_error = np.sqrt(np.power(rel_error_1, 2) + np.power(rel_error_2, 2))
102 |
103 | print(f"peak 1 x: {x_peak1}")
104 | print(f"peak 2 x: {x_peak2}")
105 | print(f"relative error peak 1: {rel_error_1}")
106 | print(f"relative error peak 2: {rel_error_2}")
107 | print(f"relative error: {rel_error}")
108 |
109 | return rel_error
110 |
111 | def test_gen_config(sim_path):
112 | assert gen_config(sim_path)
113 |
114 | def test_run_sim(sim_path):
115 | assert run_sim(sim_path)
116 |
117 | def test_is_decaying(sim_path):
118 | assert is_decaying(sim_path)
119 |
120 | def test_compare_previous_peaks(sim_path):
121 | assert compare_previous_peaks(sim_path) < 1E-6
122 |
123 |
--------------------------------------------------------------------------------
/tests/combined/test_clamped_buckling_sigma80.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy.signal import find_peaks
3 |
4 | from pathlib import Path
5 |
6 | from skelly_sim.reader import TrajectoryReader
7 | from skelly_sim.skelly_config import Config, Fiber, Point
8 | from skelly_sim.testing import working_directory, sim_path, run_sim
9 |
10 | np.random.seed(100)
11 | config_file = 'skelly_config.toml'
12 |
13 | def gen_config(path: Path):
14 | print("Generating config")
15 | # create a config object and set the system parameters
16 | config = Config()
17 | config.params.eta = 1.0
18 | config.params.dt_initial = 0.02
19 | config.params.dt_min = 0.01
20 | config.params.dt_max = 0.1
21 | config.params.dt_write = 0.1
22 | config.params.t_final = 50.0
23 | config.params.gmres_tol = 1E-10
24 | config.params.seed = 130319
25 | config.params.pair_evaluator = "CPU"
26 | config.params.adaptive_timestep_flag = True
27 |
28 | sigma = 80.0
29 | length = 1.0
30 | bending_rigidity = 0.0025
31 | force_scale = -sigma * bending_rigidity / length**3
32 | n_nodes = 32
33 |
34 | config.fibers = [Fiber(
35 | force_scale=force_scale,
36 | length=length,
37 | n_nodes=n_nodes,
38 | bending_rigidity=bending_rigidity,
39 | minus_clamped=True
40 | )]
41 |
42 | # Give a small starting kick
43 | config.point_sources = [
44 | Point(
45 | position=[0.0, 0.0, 10*length],
46 | force=[10.0, 0.0, 0.0],
47 | time_to_live=1.0,
48 | )
49 | ]
50 |
51 | # Orient in Z
52 | config.fibers[0].x = np.linspace([0, 0, 0], [0, 0, length], n_nodes).ravel().tolist()
53 |
54 | config.save(path / config_file)
55 |
56 | return True
57 |
58 | def is_decaying(path: Path=Path('.')):
59 | with working_directory(path):
60 | traj = TrajectoryReader('skelly_config.toml')
61 |
62 | x = []
63 | for i in range(len(traj)):
64 | traj.load_frame(i)
65 | x.append(traj['fibers'][0]['x_'][-1, 0])
66 |
67 | x = np.array(x)
68 | mpeaks, _ = find_peaks(x, height=0)
69 |
70 | # Check the peaks, ignoring the first one from the original 'kick' to the system
71 | x_peak1 = x[mpeaks[1]]
72 | x_peak2 = x[mpeaks[2]]
73 | print(f"peak 1 x: {x_peak1}")
74 | print(f"peak 2 x: {x_peak2}")
75 |
76 | # Just return if the solution is decaying at this timescale
77 | return x_peak2 < x_peak1
78 |
79 | def compare_previous_peaks(path: Path=Path('.')):
80 | print(f"Comparing deflection to previous results")
81 | with working_directory(path):
82 | traj = TrajectoryReader('skelly_config.toml')
83 |
84 | x = []
85 | for i in range(len(traj)):
86 | traj.load_frame(i)
87 | x.append(traj['fibers'][0]['x_'][-1, 0])
88 |
89 | x = np.array(x)
90 | mpeaks, _ = find_peaks(x, height=0)
91 |
92 | # Check the peaks, ignoring the first one from the original 'kick' to the system, compared to previous results
93 | x_peak1 = x[mpeaks[1]]
94 | x_peak2 = x[mpeaks[2]]
95 |
96 | x_peak1_test = 0.09575812
97 | x_peak2_test = 0.13564472
98 |
99 | rel_error_1 = abs(1 - x_peak1/x_peak1_test)
100 | rel_error_2 = abs(1 - x_peak2/x_peak2_test)
101 | rel_error = np.sqrt(np.power(rel_error_1, 2) + np.power(rel_error_2, 2))
102 |
103 | print(f"peak 1 x: {x_peak1}")
104 | print(f"peak 2 x: {x_peak2}")
105 | print(f"relative error peak 1: {rel_error_1}")
106 | print(f"relative error peak 2: {rel_error_2}")
107 | print(f"relative error: {rel_error}")
108 |
109 | return rel_error
110 |
111 | def test_gen_config(sim_path):
112 | assert gen_config(sim_path)
113 |
114 | def test_run_sim(sim_path):
115 | assert run_sim(sim_path)
116 |
117 | def test_is_decaying(sim_path):
118 | assert not is_decaying(sim_path)
119 |
120 | def test_compare_previous_peaks(sim_path):
121 | assert compare_previous_peaks(sim_path) < 1E-6
122 |
123 |
--------------------------------------------------------------------------------
/tests/combined/test_fiber_const_force.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | from pathlib import Path
4 |
5 | from skelly_sim.reader import TrajectoryReader
6 | from skelly_sim.skelly_config import Config, Fiber
7 | from skelly_sim.testing import working_directory, sim_path, run_sim
8 |
9 | np.random.seed(100)
10 | config_file = 'skelly_config.toml'
11 |
12 | def gen_config(path: Path):
13 | print("Generating config")
14 | # create a config object and set the system parameters
15 | config = Config()
16 | config.params.eta = 0.7
17 | config.params.dt_initial = 1E-4
18 | config.params.dt_min = 1E-4
19 | config.params.dt_max = 1E-4
20 | config.params.dt_write = 1E-3
21 | config.params.t_final = 1E-2
22 | config.params.gmres_tol = 1E-10
23 | config.params.seed = 130319
24 | config.params.pair_evaluator = "CPU"
25 |
26 | length = 0.75
27 | config.fibers = [Fiber(
28 | force_scale=0.31,
29 | length=length,
30 | n_nodes=8,
31 | bending_rigidity=0.0025
32 | )]
33 | config.fibers[0].fill_node_positions(np.array([0.0, 0.0, 0.0]), np.array([0.0, 0.0, 1.0]))
34 |
35 | config.save(path / config_file)
36 |
37 | return True
38 |
39 |
40 | def velocity(path: Path=Path('.')):
41 | print("Comparing velocity to analytic result")
42 | with working_directory(path):
43 | traj = TrajectoryReader('skelly_config.toml')
44 |
45 | traj.load_frame(0)
46 | x0 = traj['fibers'][0]['x_'][0, :]
47 | traj.load_frame(-1)
48 | xf = traj['fibers'][0]['x_'][0, :]
49 |
50 | dt = traj.times[-1] - traj.times[0]
51 | v = (xf - x0) / dt
52 |
53 | fib = traj.config_data['fibers'][0]
54 | length = fib['length']
55 | radius = fib['radius']
56 |
57 | epsilon = radius / length
58 |
59 | gamma = fib["force_scale"] * length / v[-1]
60 | gamma_theory = -4 * np.pi * length * traj.config_data['params']['eta'] / (np.log(np.exp(1) * epsilon**2))
61 | rel_error = abs(1 - gamma/gamma_theory)
62 |
63 | print("theoretical drag: {}".format(gamma_theory))
64 | print("measured drag: {}".format(gamma))
65 | print("relative error: {}".format(rel_error))
66 |
67 | return rel_error
68 |
69 |
70 | def test_gen_config(sim_path):
71 | assert gen_config(sim_path)
72 |
73 | def test_run_sim(sim_path):
74 | assert run_sim(sim_path)
75 |
76 | def test_velocity(sim_path):
77 | assert velocity(sim_path) < 1E-6
78 |
--------------------------------------------------------------------------------
/tests/combined/test_fiber_dualfilament.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | from pathlib import Path
4 |
5 | from skelly_sim.reader import TrajectoryReader
6 | from skelly_sim.skelly_config import Config, Fiber, perturbed_fiber_positions
7 | from skelly_sim.testing import working_directory, sim_path, run_sim
8 |
9 | np.random.seed(100)
10 | config_file = 'skelly_config.toml'
11 |
12 | def gen_config(path: Path):
13 | print("Generating config")
14 | # create a config object and set the system parameters
15 | config = Config()
16 | config.params.eta = 1.0
17 | config.params.dt_initial = 1E-1
18 | config.params.dt_write = 1.0
19 | config.params.t_final = 1E1
20 | config.params.gmres_tol = 1E-10
21 | config.params.seed = 130319
22 | config.params.pair_evaluator = "CPU"
23 | config.params.adaptive_timestep_flag = False
24 |
25 | sigma = 0.0225
26 | length = 2.0
27 | bending_rigidity = 0.0025
28 | n_nodes = 64
29 |
30 | n_fibers = 2
31 |
32 | config.fibers = [Fiber(
33 | force_scale=-sigma,
34 | length=length,
35 | n_nodes=n_nodes,
36 | bending_rigidity=bending_rigidity,
37 | minus_clamped=True
38 | ) for i in range(n_fibers)]
39 |
40 | # Disrupt the first fiber only
41 | x = perturbed_fiber_positions(0.01, length, np.array([0.0, 0.0, 0.0]), np.array([0.0, 0.0, 1.0]), n_nodes, np.array([1.0, 0.0, 0.0]))
42 | config.fibers[0].x = x.ravel().tolist()
43 | config.fibers[1].fill_node_positions(np.array([1.0, 0.0, 0.0]), np.array([0.0, 0.0, 1.0]))
44 |
45 | config.save(path / config_file)
46 |
47 | return True
48 |
49 |
50 | def deflection(path: Path=Path('.')):
51 | print(f"Comparing deflection to previous results")
52 | with working_directory(path):
53 | traj = TrajectoryReader('skelly_config.toml')
54 |
55 | traj.load_frame(-1)
56 | x0 = traj['fibers'][0]['x_'][-1,0]
57 | x1 = traj['fibers'][1]['x_'][-1,0]
58 |
59 | # The deflection of the fiber(s) from previous results with these parameter choices
60 | x0_test = -0.004765810967995735
61 | x1_test = 1.0048647877439878
62 |
63 | rel_error_0 = abs(1 - x0/x0_test)
64 | rel_error_1 = abs(1 - x1/x1_test)
65 | rel_error = np.sqrt(np.power(rel_error_0, 2) + np.power(rel_error_1, 2))
66 |
67 | print(f"x0: {x0}")
68 | print(f"x1: {x1}")
69 | print(f"relative error x0 (driver): {rel_error_0}")
70 | print(f"relative error x1 (response): {rel_error_1}")
71 | print(f"relative error: {rel_error}")
72 |
73 | return rel_error
74 |
75 |
76 | def test_gen_config(sim_path):
77 | assert gen_config(sim_path)
78 |
79 | def test_run_sim(sim_path):
80 | assert run_sim(sim_path)
81 |
82 | def test_deflection(sim_path):
83 | assert deflection(sim_path) < 1E-6
84 |
--------------------------------------------------------------------------------
/tests/combined/test_fiber_uniform_background.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | from pathlib import Path
4 |
5 | from skelly_sim.reader import TrajectoryReader
6 | from skelly_sim.skelly_config import Config, Fiber
7 | from skelly_sim.testing import working_directory, sim_path, run_sim
8 |
9 | np.random.seed(100)
10 | config_file = 'skelly_config.toml'
11 |
12 | def gen_config(path: Path):
13 | print("Generating config")
14 | # create a config object and set the system parameters
15 | config = Config()
16 | config.params.eta = 0.7
17 | config.params.dt_initial = 1E-4
18 | config.params.dt_min = 1E-4
19 | config.params.dt_max = 1E-4
20 | config.params.dt_write = 1E-3
21 | config.params.t_final = 1E-2
22 | config.params.gmres_tol = 1E-10
23 | config.params.seed = 130319
24 | config.params.pair_evaluator = "CPU"
25 |
26 | length = 0.75
27 | config.fibers = [Fiber(
28 | length=length,
29 | n_nodes=8,
30 | bending_rigidity=0.0025
31 | )]
32 | config.fibers[0].fill_node_positions(np.array([0.0, 0.0, 0.0]), np.array([0.0, 0.0, 1.0]))
33 |
34 | config.background.uniform = [1.0, 2.0, 3.0]
35 |
36 | config.save(path / config_file)
37 |
38 | return True
39 |
40 |
41 | def velocity(path: Path=Path('.')):
42 | print("Comparing velocity to analytic result")
43 | with working_directory(path):
44 | traj = TrajectoryReader('skelly_config.toml')
45 |
46 | traj.load_frame(0)
47 | x0 = traj['fibers'][0]['x_'][0, :]
48 | traj.load_frame(-1)
49 | xf = traj['fibers'][0]['x_'][0, :]
50 |
51 | dt = traj.times[-1] - traj.times[0]
52 | v = np.linalg.norm((xf - x0) / dt)
53 |
54 | velocity_meas = v
55 | velocity_theory = np.linalg.norm(np.array(traj.config_data['background']['uniform']))
56 | rel_error = 1 - velocity_meas/velocity_theory
57 |
58 | return rel_error
59 |
60 |
61 | def test_gen_config(sim_path):
62 | assert gen_config(sim_path)
63 |
64 | def test_run_sim(sim_path):
65 | assert run_sim(sim_path)
66 |
67 | def test_velocity(sim_path):
68 | assert velocity(sim_path) < 1E-13
69 |
--------------------------------------------------------------------------------
/tests/core/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | file(GLOB test_src "*.cpp")
3 |
4 | foreach(srcfile ${test_src})
5 | string(REPLACE ".cpp" "" executable ${srcfile})
6 | get_filename_component(executable ${executable} NAME)
7 | add_executable(${executable} ${srcfile})
8 | target_link_libraries(${executable} PUBLIC skelly z MPI::MPI_CXX trng4_static
9 | ${Kokkos_LIBRARIES} ${Tpetra_LIBRARIES} ${Teuchos_LIBRARIES} ${Belos_LIBRARIES})
10 | endforeach()
11 |
12 | foreach(kernel stokeslet;stresslet)
13 | foreach(driver single;openmp;gpu;fmm)
14 | add_test(NAME ${kernel}_${driver} COMMAND kernel_test --kernel=${kernel} --driver=${driver})
15 | endforeach()
16 | endforeach()
17 |
18 | ###############################################################################
19 | # Fetch google test
20 | ###############################################################################
21 | set(INSTALL_GTEST OFF)
22 | include(FetchContent)
23 | FetchContent_Declare(
24 | googletest
25 | GIT_REPOSITORY https://github.com/google/googletest.git
26 | GIT_TAG release-1.12.1
27 | )
28 |
29 | # For Windows: Prevent overriding the parent project's compiler/linker settings
30 | # https://chromium.googlesource.com/external/github.com/pwnall/googletest/+/HEAD/googletest/README.md
31 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
32 | FetchContent_MakeAvailable(googletest)
33 |
34 | # Create the test files directory
35 | set(TEST_FILES_DIR ${CMAKE_SOURCE_DIR}/tests/core/test_files)
36 |
37 | add_subdirectory(unit_tests)
38 |
--------------------------------------------------------------------------------
/tests/core/dynamic_instability_test.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include
16 | #include
17 |
18 | int main(int argc, char *argv[]) {
19 | int thread_level;
20 | MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &thread_level);
21 | int rank;
22 | MPI_Comm_rank(MPI_COMM_WORLD, &rank);
23 |
24 | std::string config_file = "skelly_config.toml";
25 | Teuchos::CommandLineProcessor cmdp(false, true);
26 | cmdp.setOption("config-file", &config_file, "TOML input file.");
27 | if (cmdp.parse(argc, argv) != Teuchos::CommandLineProcessor::PARSE_SUCCESSFUL) {
28 | MPI_Finalize();
29 | return EXIT_FAILURE;
30 | }
31 |
32 | try {
33 | System::init(config_file, false, false);
34 | auto params = System::get_params();
35 | auto &properties = System::get_properties();
36 | double t_final = params->t_final;
37 |
38 | // Only used with finite difference fibers (for now)
39 | auto &fc = *System::get_fiber_container();
40 | if (fc.fiber_type_ != FiberContainerBase::FIBERTYPE::FiniteDifference) {
41 | throw std::runtime_error("dynamic_instability_test only compatible with FiniteDifferenceFiber(s) for now");
42 | }
43 | FiberContainerFiniteDifference *fc_fd = static_cast(&fc);
44 |
45 | std::vector times;
46 | std::vector n_fibers;
47 | std::vector lengths;
48 |
49 | double t = 0.0;
50 | while (t < t_final) {
51 | times.push_back(t);
52 | n_fibers.push_back(fc_fd->get_global_fiber_count());
53 | double length =
54 | std::accumulate(fc_fd->fibers_.begin(), fc_fd->fibers_.end(), 0.0,
55 | [](const double &a, const FiberFiniteDifference &b) { return a + b.length_; });
56 | double length_tot;
57 | MPI_Reduce(&length, &length_tot, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
58 |
59 | lengths.push_back(length_tot / n_fibers.back());
60 | System::dynamic_instability();
61 | t += properties.dt;
62 | }
63 |
64 | if (rank == 0) {
65 | cnpy::npz_save("di_traj.npz", "t", times.data(), {times.size()}, "w");
66 | cnpy::npz_save("di_traj.npz", "n_fibers", n_fibers.data(), {n_fibers.size()}, "a");
67 | cnpy::npz_save("di_traj.npz", "lengths", lengths.data(), {lengths.size()}, "a");
68 | }
69 | } catch (const std::runtime_error &e) {
70 | // Warning: Critical only catches things on rank 0, so this may or may not print, if
71 | // some random rank throws an error. This is the same reason we use MPI_Abort: all
72 | // ranks are not guaranteed to land here, so there's only so much grace that can be
73 | // easily implemented.
74 | spdlog::critical(std::string("Fatal exception caught: ") + e.what());
75 | MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
76 | }
77 |
78 | MPI_Finalize();
79 | return EXIT_SUCCESS;
80 | }
81 |
--------------------------------------------------------------------------------
/tests/core/kernel_test.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include
6 | #include
7 |
8 | using Eigen::MatrixXd;
9 |
10 | int main(int argc, char *argv[]) {
11 | int thread_level;
12 | MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &thread_level);
13 |
14 | std::string kernel, driver;
15 |
16 | Teuchos::CommandLineProcessor cmdp(false, true);
17 | cmdp.setOption("kernel", &kernel, "kernel to evaluate [stokeslet, stresslet]", true);
18 | cmdp.setOption("driver", &driver, "driver to evaluate [single, openmp, gpu, fmm]", true);
19 |
20 | if (cmdp.parse(argc, argv) != Teuchos::CommandLineProcessor::PARSE_SUCCESSFUL) {
21 | MPI_Finalize();
22 | return EXIT_FAILURE;
23 | }
24 |
25 | constexpr int n_src = 1229;
26 | constexpr int n_trg = 743;
27 | constexpr double eta = 1.3;
28 |
29 | const int mult_order = 16;
30 | const int max_pts = 50;
31 |
32 | MatrixXd r_src = MatrixXd::Random(3, n_src);
33 | MatrixXd r_trg = MatrixXd::Random(3, n_trg);
34 | MatrixXd nullmat;
35 | double err;
36 | if (kernel == "stokeslet") {
37 | MatrixXd f_src = MatrixXd::Random(3, n_src);
38 |
39 | omp_set_num_threads(1);
40 | MatrixXd ref = kernels::stokeslet_direct_cpu(r_src, nullmat, r_trg, f_src, nullmat, eta);
41 | MatrixXd other;
42 |
43 | if (driver == "single") {
44 | omp_set_num_threads(1);
45 | other = kernels::stokeslet_direct_cpu(r_src, nullmat, r_trg, f_src, nullmat, eta);
46 | } else if (driver == "openmp") {
47 | omp_set_num_threads(4);
48 | other = kernels::stokeslet_direct_cpu(r_src, nullmat, r_trg, f_src, nullmat, eta);
49 | } else if (driver == "gpu") {
50 | other = kernels::stokeslet_direct_gpu(r_src, nullmat, r_trg, f_src, nullmat, eta);
51 | } else if (driver == "fmm") {
52 | auto stokeslet_kernel_fmm = kernels::FMM(mult_order, max_pts, stkfmm::PAXIS::NONE,
53 | stkfmm::KERNEL::Stokes, kernels::stokes_vel_fmm);
54 | other = stokeslet_kernel_fmm(r_src, nullmat, r_trg, f_src, nullmat, eta);
55 | } else {
56 | std::cerr << "Invalid driver supplied \"" + driver << "\"" << std::endl;
57 | return EXIT_FAILURE;
58 | }
59 |
60 | err = sqrt((ref - other).squaredNorm());
61 | } else if (kernel == "stresslet") {
62 | MatrixXd f_src = MatrixXd::Random(9, n_src);
63 |
64 | omp_set_num_threads(1);
65 | MatrixXd ref = kernels::stresslet_direct_cpu(nullmat, r_src, r_trg, nullmat, f_src, eta);
66 | MatrixXd other;
67 |
68 | if (driver == "single") {
69 | omp_set_num_threads(1);
70 | other = kernels::stresslet_direct_cpu(nullmat, r_src, r_trg, nullmat, f_src, eta);
71 | } else if (driver == "openmp") {
72 | omp_set_num_threads(4);
73 | other = kernels::stresslet_direct_cpu(nullmat, r_src, r_trg, nullmat, f_src, eta);
74 | } else if (driver == "gpu") {
75 | other = kernels::stresslet_direct_gpu(nullmat, r_src, r_trg, nullmat, f_src, eta);
76 | } else if (driver == "fmm") {
77 | auto stresslet_kernel_fmm = kernels::FMM(mult_order, max_pts, stkfmm::PAXIS::NONE,
78 | stkfmm::KERNEL::PVel, kernels::stokes_pvel_fmm);
79 | other = stresslet_kernel_fmm(nullmat, r_src, r_trg, nullmat, f_src, eta);
80 | } else {
81 | std::cerr << "Invalid driver supplied \"" + driver << "\"" << std::endl;
82 | return EXIT_FAILURE;
83 | }
84 |
85 | err = sqrt((ref - other).squaredNorm());
86 | } else {
87 | std::cerr << "Invalid kernel supplied \"" + kernel << "\"" << std::endl;
88 | return EXIT_FAILURE;
89 | }
90 |
91 | std::cout << err << std::endl;
92 | return err > 5E-9;
93 | }
94 |
--------------------------------------------------------------------------------
/tests/core/solver_test.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include
16 |
17 | int main(int argc, char *argv[]) {
18 | int thread_level;
19 | MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &thread_level);
20 | int rank;
21 | MPI_Comm_rank(MPI_COMM_WORLD, &rank);
22 |
23 | std::string config_file = "skelly_config.toml";
24 | Teuchos::CommandLineProcessor cmdp(false, true);
25 | cmdp.setOption("config-file", &config_file, "TOML input file.");
26 | if (cmdp.parse(argc, argv) != Teuchos::CommandLineProcessor::PARSE_SUCCESSFUL) {
27 | MPI_Finalize();
28 | return EXIT_FAILURE;
29 | }
30 |
31 | try {
32 | System::init(config_file, false, false);
33 | auto params = System::get_params();
34 | auto psc = System::get_point_source_container();
35 |
36 | PointSourceContainer psc_old = *psc;
37 |
38 | System::solve();
39 |
40 | psc->points.clear();
41 |
42 | double radius = System::get_shell()->node_pos_.col(0).norm();
43 | int n_nodes = System::get_shell()->n_nodes_global_;
44 |
45 | Eigen::MatrixXd r_trg = Eigen::MatrixXd::Zero(3, 100);
46 | r_trg.row(2).setLinSpaced(-radius, radius);
47 |
48 | Eigen::MatrixXd velocity_system = System::velocity_at_targets(r_trg);
49 |
50 | std::ofstream outstream("N" + std::to_string(n_nodes) + "_Rp" + std::to_string(radius) + "_Rs" +
51 | std::to_string(psc_old.points[0].position_[2]) + ".dat");
52 | Eigen::MatrixXd velocity_points = psc_old.flow(r_trg, params->eta, 0.0);
53 | for (int i = 0; i < r_trg.cols(); ++i)
54 | outstream << r_trg(2, i) << " "
55 | << (1.0 + velocity_system.col(i).array() / velocity_points.col(i).array()).abs()[2] << " "
56 | << velocity_points.col(i).transpose()[2] << std::endl;
57 |
58 | } catch (const std::runtime_error &e) {
59 | // Warning: Critical only catches things on rank 0, so this may or may not print, if
60 | // some random rank throws an error. This is the same reason we use MPI_Abort: all
61 | // ranks are not guaranteed to land here, so there's only so much grace that can be
62 | // easily implemented.
63 | spdlog::critical(std::string("Fatal exception caught: ") + e.what());
64 | MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
65 | }
66 |
67 | MPI_Finalize();
68 | return EXIT_SUCCESS;
69 | }
70 |
--------------------------------------------------------------------------------
/tests/core/test_files/body_precompute_body_ellipsoid_n1.npz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatironinstitute/SkellySim/6d54834c9488d8a357b2ebbcd9d5c6fe540cbd53/tests/core/test_files/body_precompute_body_ellipsoid_n1.npz
--------------------------------------------------------------------------------
/tests/core/test_files/ellipsoidal_body_n1.toml:
--------------------------------------------------------------------------------
1 | [[bodies]]
2 | n_nucleation_sites = 0
3 | position = [ 0.0, 0.0, 0.0,]
4 | orientation = [ 0.0, 0.0, 0.0, 1.0,]
5 | shape = "ellipsoid"
6 | radius = 1.0
7 | n_nodes = 400
8 | axis_length = [ 0.4, 0.4, 0.5,]
9 | precompute_file = "body_precompute_body_ellipsoid_n1.npz"
10 | external_force_type = "Oscillatory"
11 | external_force = [ 0.0, 0.0, 1.0,]
12 | external_torque = [ 0.0, 0.0, 0.0,]
13 | external_oscillation_force_amplitude = 2.0
14 | external_oscillation_force_frequency = 1.0
15 | external_oscillation_force_phase = 0.0
16 |
17 | [params]
18 | eta = 1.0
19 | dt_initial = 0.05
20 | dt_min = 0.0001
21 | dt_max = 0.05
22 | dt_write = 0.05
23 | t_final = 5.0
24 | gmres_tol = 1e-8
25 | fiber_error_tol = 0.1
26 | seed = 130319
27 | implicit_motor_activation_delay = 0.0
28 | periphery_interaction_flag = false
29 | adaptive_timestep_flag = true
30 | pair_evaluator = "FMM"
31 | fiber_type = "FiniteDifference"
32 |
33 | [background]
34 | components = [ 0, 0, 0,]
35 | scale_factor = [ 0.0, 0.0, 0.0,]
36 | uniform = [ 0.0, 0.0, 0.0,]
37 |
38 | [periphery]
39 | n_nodes = 2000
40 | precompute_file = "periphery_precompute.npz"
41 | shape = "sphere"
42 | radius = 3.846153846153846
43 |
44 | [params.dynamic_instability]
45 | n_nodes = 0
46 | v_growth = 0.0
47 | f_catastrophe = 0.0
48 | v_grow_collision_scale = 0.5
49 | f_catastrophe_collision_scale = 2.0
50 | nucleation_rate = 0.0
51 | radius = 0.025
52 | min_length = 0.5
53 | bending_rigidity = 0.0025
54 | min_separation = 0.1
55 |
56 | [params.periphery_binding]
57 | active = false
58 | polar_angle_start = 0.0
59 | polar_angle_end = 1.5707963267948966
60 | threshold = 0.75
61 |
--------------------------------------------------------------------------------
/tests/core/test_files/fiber_container_fdf_n1.toml:
--------------------------------------------------------------------------------
1 | [[fibers]]
2 | n_nodes = 32
3 | parent_body = -1
4 | parent_site = -1
5 | force_scale = 0.0
6 | bending_rigidity = 20.0
7 | radius = 0.01
8 | length = 20.0
9 | minus_clamped = false
10 | x = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.6451612903225806, 0.0, 0.0, 1.2903225806451613, 0.0, 0.0, 1.935483870967742, 0.0, 0.0, 2.5806451612903225, 0.0, 0.0, 3.225806451612903, 0.0, 0.0, 3.870967741935484, 0.0, 0.0, 4.516129032258064, 0.0, 0.0, 5.161290322580645, 0.0, 0.0, 5.806451612903226, 0.0, 0.0, 6.451612903225806, 0.0, 0.0, 7.096774193548388, 0.0, 0.0, 7.741935483870968, 0.0, 0.0, 8.387096774193548, 0.0, 0.0, 9.032258064516128, 0.0, 0.0, 9.67741935483871, 0.0, 0.0, 10.32258064516129, 0.0, 0.0, 10.96774193548387, 0.0, 0.0, 11.612903225806452, 0.0, 0.0, 12.258064516129032, 0.0, 0.0, 12.903225806451612, 0.0, 0.0, 13.548387096774192, 0.0, 0.0, 14.193548387096776, 0.0, 0.0, 14.838709677419356, 0.0, 0.0, 15.483870967741936, 0.0, 0.0, 16.129032258064516, 0.0, 0.0, 16.774193548387096, 0.0, 0.0, 17.419354838709676, 0.0, 0.0, 18.064516129032256, 0.0, 0.0, 18.709677419354836, 0.0, 0.0, 19.35483870967742, 0.0, 0.0, 20.0,]
11 |
12 | [params]
13 | eta = 1.0
14 | dt_initial = 0.01
15 | dt_min = 1e-5
16 | dt_max = 1.0
17 | dt_write = 0.01
18 | t_final = 10.0
19 | gmres_tol = 1e-8
20 | fiber_error_tol = 0.1
21 | seed = 130319
22 | implicit_motor_activation_delay = 0.0
23 | periphery_interaction_flag = false
24 | adaptive_timestep_flag = true
25 | pair_evaluator = "FMM"
26 |
27 | [background]
28 | components = [ 0, 0, 0,]
29 | scale_factor = [ 0.0, 0.0, 0.0,]
30 | uniform = [ 0.0, 0.0, 0.0,]
31 |
32 | [periphery]
33 | n_nodes = 6000
34 | precompute_file = "periphery_precompute.npz"
35 | shape = "sphere"
36 | radius = 6.0
37 |
38 | [params.dynamic_instability]
39 | n_nodes = 0
40 | v_growth = 0.0
41 | f_catastrophe = 0.0
42 | v_grow_collision_scale = 0.5
43 | f_catastrophe_collision_scale = 2.0
44 | nucleation_rate = 0.0
45 | radius = 0.025
46 | min_length = 0.5
47 | bending_rigidity = 0.0025
48 | min_separation = 0.1
49 |
50 | [params.periphery_binding]
51 | active = false
52 | polar_angle_start = 0.0
53 | polar_angle_end = 1.5707963267948966
54 | threshold = 0.75
55 |
--------------------------------------------------------------------------------
/tests/core/unit_tests/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | set(TEST_LIST
3 | unit_test_fiber_finite_difference
4 | unit_test_fibercontainer_finite_difference
5 | unit_test_serialization
6 | unit_test_body_ellipsoidal
7 | unit_test_skelly_chebyshev
8 | unit_test_fiber_chebyshev_penalty_autodiff
9 | )
10 |
11 | # Setup the MPI testing environment for the MPI tests
12 | # Taken from hoomd-blue v4.0.1
13 | MACRO(ADD_TO_MPI_TESTS _KEY _VALUE)
14 | SET("NProc_${_KEY}" "${_VALUE}")
15 | SET(MPI_TEST_LIST ${MPI_TEST_LIST} ${_KEY})
16 | ENDMACRO(ADD_TO_MPI_TESTS)
17 |
18 | ADD_TO_MPI_TESTS(unit_test_fibercontainer_finite_difference_mpi 2)
19 | ADD_TO_MPI_TESTS(unit_test_serialization_mpi 2)
20 |
21 | # Set up the test executables
22 | foreach (CUR_TEST ${TEST_LIST} ${MPI_TEST_LIST})
23 | # Add the executable
24 | add_executable(${CUR_TEST} EXCLUDE_FROM_ALL ${CUR_TEST}.cpp)
25 |
26 | target_compile_definitions(${CUR_TEST} PRIVATE TEST_FILES_DIR="${TEST_FILES_DIR}")
27 |
28 | add_dependencies(test_all ${CUR_TEST})
29 |
30 | target_link_libraries(${CUR_TEST} PRIVATE
31 | skelly
32 | GTest::gtest
33 | GTest::gtest_main
34 | GTest::gmock
35 | GTest::gmock_main)
36 | target_include_directories(${CUR_TEST} PRIVATE
37 | "${googletest_SOURCE_DIR}/googlemock/include"
38 | "${googletest_SOURCE_DIR}/googletest/include")
39 | endforeach (CUR_TEST)
40 |
41 | # Add the non-mpi tests first (1 processor)
42 | foreach (CUR_TEST ${TEST_LIST})
43 | add_test(NAME ${CUR_TEST} COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 1 ${MPIEXEC_POSTFLAGS} $)
44 | endforeach (CUR_TEST)
45 |
46 | # Add the mpi tests (variable processors)
47 | foreach (CUR_TEST ${MPI_TEST_LIST})
48 | set(MPI_TEST_NAME mpi-${CUR_TEST})
49 |
50 | add_test(NAME ${MPI_TEST_NAME} COMMAND
51 | ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG}
52 | ${NProc_${CUR_TEST}} ${MPIEXEC_POSTFLAGS}
53 | $)
54 | endforeach (CUR_TEST)
55 |
--------------------------------------------------------------------------------
/tests/core/unit_tests/mpi_environment.hpp:
--------------------------------------------------------------------------------
1 | #ifndef TESTS_MPI_ENVIRONMENT_HPP_
2 | #define TESTS_MPI_ENVIRONMENT_HPP_
3 |
4 | /// \file mpi_environment.hpp
5 | /// \brief Test MPI environment controller
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | // External includes
12 | #include
13 | #include
14 | #include
15 |
16 | /// \class MPIEnvironment
17 | /// \brief MPI environment interface with google test
18 |
19 | class MPIEnvironment : public ::testing::Environment {
20 | public:
21 | virtual void SetUp() {
22 | char **argv;
23 | int argc = 0;
24 | int mpiError = MPI_Init(&argc, &argv);
25 | ASSERT_FALSE(mpiError);
26 |
27 | // Load the MPI configuration for spdlog for our application
28 | int mrank;
29 | int msize;
30 | MPI_Comm_rank(MPI_COMM_WORLD, &mrank);
31 | MPI_Comm_size(MPI_COMM_WORLD, &msize);
32 | spdlog::logger sink =
33 | mrank == 0 ? spdlog::logger("SkellySim", std::make_shared())
34 | : spdlog::logger("SkellySim", std::make_shared());
35 | spdlog::set_default_logger(std::make_shared(sink));
36 | spdlog::stderr_color_mt("STKFMM");
37 | spdlog::stderr_color_mt("Belos");
38 | spdlog::stderr_color_mt("SkellySim global");
39 | spdlog::cfg::load_env_levels();
40 | }
41 |
42 | virtual void TearDown() {
43 | int mpiError = MPI_Finalize();
44 | ASSERT_FALSE(mpiError);
45 | }
46 |
47 | virtual ~MPIEnvironment() {}
48 | }; // MPIEnvironment
49 |
50 | #endif // TESTS_MPI_ENVIRONMENT_HPP_
51 |
--------------------------------------------------------------------------------
/tests/core/unit_tests/unit_test_body_ellipsoidal.cpp:
--------------------------------------------------------------------------------
1 | /// \file unit_test_fiber_finite_difference.cpp
2 | /// \brief Unit tests for FiberFiniteDifference (single MPI rank)
3 |
4 | // C++ includes
5 | #include
6 |
7 | // skelly includes
8 | #include
9 | #include
10 |
11 | // test files
12 | #include "./mpi_environment.hpp"
13 |
14 | int main(int argc, char **argv) {
15 | ::testing::InitGoogleTest(&argc, argv);
16 | ::testing::AddGlobalTestEnvironment(new MPIEnvironment);
17 | return RUN_ALL_TESTS();
18 | }
19 |
20 | TEST(EllipsoidalBody, ConstructorTest) {
21 |
22 | // Get the ellipsoidal bodies read in
23 | const std::string toml_file = std::string(TEST_FILES_DIR) + "/ellipsoidal_body_n1.toml";
24 | auto param_table = toml::parse(toml_file);
25 | auto params = Params(param_table.at("params"));
26 | params.print();
27 |
28 | // Construct the body container for this, should handle the abstraction itself
29 | // FIXME XXX Currently we have issues loading in the quadrature points on the body from a separate directory
30 | BodyContainer bc = BodyContainer();
31 | }
32 |
--------------------------------------------------------------------------------
/tests/core/unit_tests/unit_test_fiber_finite_difference.cpp:
--------------------------------------------------------------------------------
1 | /// \file unit_test_fiber_finite_difference.cpp
2 | /// \brief Unit tests for FiberFiniteDifference (single MPI rank)
3 |
4 | // C++ includes
5 | #include
6 |
7 | // skelly includes
8 | #include
9 |
10 | // test files
11 | #include "./mpi_environment.hpp"
12 |
13 | int main(int argc, char **argv) {
14 | ::testing::InitGoogleTest(&argc, argv);
15 | ::testing::AddGlobalTestEnvironment(new MPIEnvironment);
16 | return RUN_ALL_TESTS();
17 | }
18 |
19 | TEST(FiberFiniteDifference, ConstructorTest) {
20 | double flength = 1.0;
21 | double bending_rigidity = 0.0025;
22 | double radius = 0.0125;
23 | double eta = 1.0;
24 | int n_nodes = 32;
25 | FiberFiniteDifference fiber(n_nodes, radius, flength, bending_rigidity, eta);
26 |
27 | // Do some simple assertions to test if we have the right number of columns, etc
28 | ASSERT_EQ(3, fiber.x_.rows());
29 | ASSERT_EQ(n_nodes, fiber.x_.cols());
30 | }
31 |
--------------------------------------------------------------------------------