Created: 2021-04-13 Tue 04:58
docs/pres
git log
Readme.{md,org}
LICENSE
.gitignore
$ git init # Inside project $ gibo macOS Windows Xcode Emacs \ Vim Python C++ \ CMake TeX > .gitignore $ touch readme.md $ license-generator MIT \ --author "Person" $ tree -L 2 . ├── LICENSE ├── docs │ └── pres └── readme.org 2 directories, 2 files
src/
include/
docs/
CMakeLists.txt
ci/
// Along each -I and INCLUDE #include<iostream>
// Starts in the same directory // Reverse order upward // -I and INCLUDE #include "path-spec"
rm -rf build
# --- Variables if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release endif() set(CMAKE_CXX_FLAGS "-Wall -Wextra") set(CMAKE_CXX_FLAGS_DEBUG "-g") set(CMAKE_CXX_FLAGS_RELEASE "-O3")
$ # Should fail!!! $ cmake .
cmake_minimum_required(VERSION 3.14 FATAL_ERROR) # ---- Project ---- project( PenningTrapSimulationII VERSION 1.0 LANGUAGES CXX ) # ---- Include guards ---- if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) message( FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there." ) endif()
$ # Works $ cmake -H. -Bbuild $ cmake --build build $ cd build $ simulation.exe
#ifndef IOP_VEC_H #define IOP_VEC_H ... #endif /* IOP_VEC_H */
# --- Library set(headers "${CMAKE_CURRENT_SOURCE_DIR}/include/math_types/iop_vec.hpp") set(sources "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp") # Build add_executable(simulation.exe ${sources}) # Add to INCLUDE target_include_directories( simulation.exe PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> )
<>
to ""
for includes#include <math_types/iop_vec.hpp>
using
namespace iopdat { // data_types/iop_particle.hpp class Particle { public: Vector3 position; ... } }
namespace iopdat { // data_types/iop_vec.hpp class Vector3 { ... } }
set(headers "include/data_types/iop_vec.hpp" "include/data_types/iop_particle.hpp" )
#include <data_types/iop_vec.hpp> #include <data_types/iop_particle.hpp>
data_types
math_types
wasn’t primitive enoughBefore
class TimeStepper { private: std::vector<PhysicsProcess *> physics_list; iopdat::Particle p; double dt; public: TimeStepper(double time_step_size, double charge, double mass) : p(charge, mass) { dt = time_step_size; } ... };
After
class TimeStepper { private: std::vector<PhysicsProcess *> physics_list; iopdat::Particle p; double dt; public: TimeStepper(double time_step_size, double charge, double mass); void AddProcess(PhysicsProcess* process); void Setup(iopdat::Vector3 position, iopdat::Vector3 Velocity); void Step(); void Print(); };
Header Only
Compiled
$ tree -L . . ├── CMakeLists.txt ├── LICENSE ├── docs │ └── pres ├── include │ ├── data_types │ └── phys_procs.hpp ├── libsrc │ └── phys_procs.cpp ├── readme.org └── src ├── CMakeLists.txt └── main.cpp 6 directories, 7 files
.hpp
/** * @brief Takes a step * @returns None. */ void Step();
.cpp
/** * @detail The algorithm is a direct interpretation * of standard mechanics */ void physproc::TimeStepper::Step() { iopdat::Vector3 F(0, 0, 0); for (int i = 0; i < physics_list.size(); i++) { F += physics_list.at(i)->Force(p); } iopdat::Vector3 dv(dt * F.x / p.mass, dt * F.y / p.mass, dt * F.z / p.mass); p.velocity += dv; p.position += iopdat::Vector3(p.velocity.x * dt, p.velocity.y * dt, p.velocity.z * dt); }
add_library(ioplib INTERFACE)
// main.cpp ... #include <phys_procs.hpp>
# Build order add_dependencies(simulation.exe ioplib) # Libraries target_link_libraries(simulation.exe ioplib) # Dependencies add_library( ioplib SHARED "libsrc/phys_procs.cpp" ) # Still need the older headers target_include_directories( ioplib PUBLIC $<BUILD_INTERFACE: ${PROJECT_SOURCE_DIR}/include> )
boost
is a classic example of a mostly compiled libraryEigen3
is famously header-onlyLet there be libraries!! — Rohit Goswami (2021)
Python
poetry
, pipenv
, pyenv
C++
conan
, vcpkg
, cpm
mkdir -p cmake wget -O cmake/CPM.cmake "https://github.com/cpm-cmake/\ CPM.cmake/releases/latest/download/get_cpm.cmake" # Can get slow otherwise, rebuilds constantly export CPM_SOURCE_CACHE=$HOME/.cache/CPM
# --- Helpers include(cmake/CPM.cmake) CPMUsePackageLock(package-lock.cmake)
# We like locks cmake -H. -Bbuild cmake --build build --target cpm-update-package-lock
C++
has great testing frameworks
Catch2
, googletest
, doctest
, etc.# Catch2 CMakeLists.txt include(CTest) add_subdirectory(tests) enable_testing()
# --- Externals ./tests/CMakeLists.txt include(../cmake/CPM.cmake) CPMAddPackage("gh:catchorg/Catch2@2.13.4") add_executable(particle_tests main.cpp vector_particle-test.cpp phys_processes-test.cpp ) # Link everything target_link_libraries(particle_tests ioplib Catch2) target_compile_features(particle_tests PRIVATE cxx_std_17) # Project Libraries include_directories( ../src/include/data_types ${PROJECT_SOURCE_DIR}/src/include/) # ---- Run tests ---- add_test(NAME particleIOP-unit-tests COMMAND $<TARGET_FILE:particle_tests>)
#define CATCH_CONFIG_MAIN #define CATCH_CONFIG_RUNNER #include <catch2/catch.hpp>
#include <catch2/catch.hpp> #include <data_types/iop_particle.hpp> #include <data_types/iop_vec.hpp> #include <phys_procs.hpp> #include <iostream> TEST_CASE("Basic Vector class tests", "Vector3") { iopdat::Vector3 a{1, 2, 3}; REQUIRE(sizeof(a) == 24); REQUIRE(sizeof(a) / sizeof(1) == 6); REQUIRE(sizeof(a) / sizeof(1.0) == 3); iopdat::Vector3 b{4, 5, 6}; b += a; REQUIRE(b.x == 5); REQUIRE(b.y == 7); REQUIRE(b.z == 9); std::cout << b.x; }
nixpkgs
can take over a day!docker
or nix
based systems
nix
name: CMake on: [push] env: BUILD_TYPE: Release jobs: build: runs-on: ${{ matrix.OS }} name: "${{ matrix.BUILD_TYPE }}" strategy: matrix: include: - BUILD_TYPE: Debug OS: ubuntu-latest - BUILD_TYPE: Debug OS: macos-latest - BUILD_TYPE: Debug ARCH: x86_64 OS: windows-latest # broken
steps: - uses: actions/checkout@v2 - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build - name: Configure CMake shell: bash working-directory: ${{github.workspace}}/build run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE - name: Build working-directory: ${{github.workspace}}/build shell: bash run: cmake --build . --config $BUILD_TYPE - name: Test working-directory: ${{github.workspace}}/build shell: bash run: ctest -C $BUILD_TYPE
simulate.exe > someFile.txt
import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation import pandas as pd df = pd.read_csv("someFile.txt", sep=" ") # Do stuff now
lua
to expose a scripting engine
C++
[goswamiDSEAMSDeferredStructural2020]
pip install matplotlib
CPMAddPackage( NAME pybind11 GITHUB_REPOSITORY pybind/pybind11 GIT_TAG v2.6.1 ) target_link_libraries(simulation.exe ioplib pybind11::embed)
// py::scoped_interpreter #include <pybind11/embed.h> #include <pybind11/stl.h> namespace py = pybind11; int main() { std::vector<double> signal(1024); for (size_t i = 0; i < signal.size(); ++i) signal[i] = std::exp(i / -256.0) * std::cos(2 * M_PI * 8 * i / 1024.0);
py::scoped_interpreter guard{}; using namespace py::literals; // Save the necessary local variables // in a Python dict py::dict locals = py::dict{ "signal"_a = signal, }; // Execute Python code, using the variables // saved in `locals` py::exec(R"( import matplotlib.pyplot as plt plt.plot(signal) plt.show() )", py::globals(), locals); }
// Getters double physproc::TimeStepper::getX() { return p.position.x; }
// Accumulators std::vector<double> x, y, z; for (int i = 0; i < 1E3; i++) { ProtonStepper.Step(); if (i % 7 == 0) { x.push_back(ProtonStepper.getX()); ... } } py::scoped_interpreter guard{}; using namespace py::literals; py::dict locals = py::dict{ "x"_a = x, ... }; py::exec(R"( )", py::globals(), locals);
from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.scatter(x, y, z, c='r', marker='o') plt.show()