# Quick Start 

This document provides a minimum example to create a simulation project using MASS CUDA and execute it on a single machine.


## Step 1: Create a CMake project

Create a directory for your project with the following structure:

```
simulation/
├── src/
│   ├── main.cu
│   └── MyPlace.h
└── CMakeLists.txt
```

In `CMakeLists.txt`, add the following content to set up the project and link with MASS CUDA:

```cmake
cmake_minimum_required(VERSION 3.20)

project(simulation LANGUAGES C CXX CUDA)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(MASS_CUDA_REPO_URL "https://bitbucket.org/mass_library_developers/mass_cuda_core")
set(MASS_CUDA_VERSION "feature/multi-gpus-multi-hosts")

include(FetchContent)
FetchContent_Declare(
    mass_cuda
    GIT_REPOSITORY ${MASS_CUDA_REPO_URL}
    GIT_TAG ${MASS_CUDA_VERSION}
)
FetchContent_MakeAvailable(mass_cuda)

add_executable(simulation 
    src/main.cu 
    src/MyPlace.cu
)
target_include_directories(simulation PRIVATE src)
target_link_libraries(simulation PRIVATE mass_cuda)
set_target_properties(simulation PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
```


## Step 2: Implement your simulation

In `MyPlace.h`, define your custom behavior for each place instance. You can separate the implementation into a additional `.cu` file if needed, but we use header-only style here for simplicity.

```C++
#pragma once

#include <mass/Place.h>

class MyPlace : public mass::Place {
public: 
    __universal__ explicit MyPlace(const mass::Index localIndex) : mass::Place(localIndex) {}

    enum ATTRIBUTE {
        ATTR
    }

    enum FUNCTION {
        FUNC
    }

    __device__ void callMethod(const mass::FunctionId funcId, void*const arg) override {
        switch (funcId) {
            case FUNC:
                func(*static_cast<const int*>(arg));
                break;
            default: 
                break;
        }
    }

    __device__ void func(const int arg) {
        const auto attrPtr = getAttribute<int>(ATTR);
        *attrPtr += arg;
    }
};
```

Then in `main.cu`, set up the simulation and define the execution loop.

```C++
#include <mass/Mass.h>
#include <iostream>
#include "MyPlace.h"

int main() {

    constexpr int numDevices = 2;  // set it to the number of GPUs you have
    constexpr int size = 100;

    mass::Mass::init();
    MASS_SCOPE_DEFER() { mass::Mass::finish(); };

    const auto places = mass::Mass::createPlaces<MyPlace>(0, {
        .size = { size, size },
        .ghostSize = 0
    });

    places->setAttribute<int>(MyPlace::ATTRIBUTE::ATTR, 1, 0);
    places->finalizeAttributes();

    for (auto step = 0; step < 1000; ++step) {
        places->callAll(MyPlace::FUNCTION::FUNC, 2);
    }

    const auto attr = places->downloadAttributes<int>(MyPlace::ATTRIBUTE::ATTR);
    for (auto deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex) {
        for (auto i = 0; i < size * size / numDevices; ++i) {
            std::cout << attr[deviceIndex][i] << " ";
        }
        std::cout << std::endl;
    }

    return 0;

}
```

## Step 3: Build and run

To build the project, run the following commands in the terminal:

```sh
mkdir build
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
```

After that, the binary executable will be in the `build` directory. You can run it with:

```sh
./build/simulation
```