diff --git a/.github/workflows/address-sanitizer.yml b/.github/workflows/address-sanitizer.yml index ab541db1..0e9ece89 100644 --- a/.github/workflows/address-sanitizer.yml +++ b/.github/workflows/address-sanitizer.yml @@ -23,7 +23,7 @@ jobs: - name: Install Dependencies # install libxerces - run: sudo apt install libxerces-c-dev + run: sudo apt install -y libxerces-c-dev libomp-dev - name: run asan # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. diff --git a/.github/workflows/cmake-build.yml b/.github/workflows/cmake-build.yml index 802a2446..d7a1a579 100644 --- a/.github/workflows/cmake-build.yml +++ b/.github/workflows/cmake-build.yml @@ -31,14 +31,13 @@ jobs: run: sudo apt install libxerces-c-dev - name: Setup buildDir - run: mkdir buildDir && cd buildDir && mkdir Release && cd Release + run: mkdir buildDir && cd buildDir - name: Configure CMake - run: cd buildDir/Release && cmake ../../CMakeLists.txt && cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON . + run: cd buildDir && cmake ../CMakeLists.txt && cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON . - name: Build - run: cd buildDir/Release && cmake --build . -- -"j$(nproc)" + run: cd buildDir && cmake --build . -- -"j$(nproc)" - name: Test - run: cd buildDir/Release/tests && ctest - + run: cd buildDir/tests && ctest \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 56d78ac6..904cd511 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,14 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -add_compile_options(-O3 -ffast-math -march=native -std=c++17 -Wno-deprecated-declarations) +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + add_compile_options(-O3 -ffast-math -march=native -std=c++17 -Wno-deprecated-declarations) +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_compile_options(-O3 -ffast-math -march=native -std=c++17 -Wno-deprecated-declarations) +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + message("Using intel compile options") + add_compile_options(-xHost -ipo -qopenmp -O3 -fp-model fast=2) +endif () list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") include(googletest) @@ -37,11 +44,12 @@ endif () # Enable DEBUG macro if (CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_options(-ggdb3) add_compile_definitions(DEBUG) message("Debug mode is enabled") endif () -option(ENABLE_BENCHMARK "Enable benchmark mode" ON) +option(ENABLE_BENCHMARK "Enable benchmark mode" OFF) if (ENABLE_BENCHMARK) message("Benchmark mode is enabled") diff --git a/Doxyfile b/Doxyfile index a4733151..b6abb684 100644 --- a/Doxyfile +++ b/Doxyfile @@ -557,7 +557,7 @@ WARN_FORMAT = "$file:$line: $text" # and error messages should be written. If left blank the output is written # to stderr. -WARN_LOGFILE = +WARN_LOGFILE = warnings.log #--------------------------------------------------------------------------- # configuration options related to the input files @@ -568,11 +568,11 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../../src \ - ../../README.md \ +INPUT = ./../src \ + ./../README.md \ -USE_MDFILE_AS_MAINPAGE = ../../README.md +USE_MDFILE_AS_MAINPAGE = ./../README.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -649,7 +649,7 @@ EXAMPLE_RECURSIVE = YES # directories that contain image that are included in the documentation (see # the \image command). -IMAGE_PATH = ../../benchmark/graph.png +IMAGE_PATH = ./../benchmark/graph.png # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program diff --git a/README.md b/README.md index 546b9fed..56e18b92 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,19 @@ MolSim - Group A === +### Slides: + +- **[Assignment 1](slides/assignment_1_slides.pdf):** Intro +- **[Assignment 2](slides/assignment_2_slides.pdf):** Performance and Tests +- **[Assignment 3](slides/assignment_3_slides.pdf):** Linked Cells Container +- **[Assignment 4](slides/assignment_4_slides.pdf):** Boundary Conditions & SuperMUC +- **[Assignment 5](slides/assignment_5_slides.pdf):** Parallelization Schemes + + ## Dependencies -- Cmake 3.24 -- Doygen 1.9.8 (`sudo apt install doxygen`) +- Cmake 3.28.3 +- Doxygen 1.9.8 (`sudo apt install doxygen`) - Libxerces (`sudo apt install libxerces-c-dev`) ## Build @@ -12,34 +21,83 @@ MolSim - Group A ### Configuration - Install - ```bash + +```bash git clone https://github.com/jkr11/MolSim.git - ``` +``` + +- manual build (I would recommend using make with -j $(nproc) as this is pretty slow) + +```bash + mkdir build + cmake -S . -B build - + cd build + make -j $(nproc) +``` + +- Testing: + +```bash + cmake -S . -B build -DBUILD_TESTS=ON +``` + +To build with Intel's C++ compiler (```icx```/```icpx```), set the following options (currently this is on the [ +`coolmuc_test`](https://github.com/jkr11/MolSim/tree/coolmuc_test) branch, but it works locally) + +```bash + cmake -S . -B build -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx +``` + +for other compilers add the necessary compiler id to the options. + +```bash + cd build/tests + make -j $(nproc) + ctest +``` + +Using the build script in `/script/` + - Build the project using the provided build script by using source, add `-t` to also build and run tests, add `-b` to - enable the BENCHMARK cmake macro - ```bash + enable the BENCHMARK cmake macro and `-p` to enable OpenMP. + +```bash cd MolSim/scripts - source build [-t|--test] [-b|--benchmark] - ``` + source build [-t|--test] [-b|--benchmark] [-p|--parallel] +``` + - Set the Input file by selecting the corresponding number during the script execution - ```bash + +```bash source set-input - ``` +``` - Creating documentation when doxygen is installed (has to be executed in the specific `buildDir/`) - ```bash + +```bash (cd ../buildDir/ when starting from /scripts) make doc_doxygen - ``` +``` + - Running the program - ```bash + +```bash $BUILD -f $INPUT - ``` + ``` + - `$BUILD` contains the location of the last compiled executable - `$INPUT` contains the location of the selected input file - Please note that `$BUILD` and `$INPUT` are only available if the scripts are executed via source. -### Options +### CMake Options + +```console + - ENABLE_BENCHMARK : enables benchmark mode with no printing of info and writing to files + - BUILD_TESTS : enables and builds the tests/ directory in the build directory + - ENABLE_OPENMP : enables OpenMP and support for multithreading +``` + +### Executable Options ```console Options: @@ -48,22 +106,62 @@ MolSim - Group A [--step_size | -s ] Specify how often the output will be written wrt. time(step_size), default=1 Note that this is independent of the time resolution (t_delta) and dependent on the simulation time [--loglevel | -l ] Specify the log level, default=info, valid=[off, error, warn, info, debug, trace] - [--checkpoint | -c ] Specifies if the final particle state will be saved to a checkpoint file. + [--checkpoint | -c ] Specifies the path the final state of the particles will be saved to. Example usage: $BUILD -f $INPUT -l -s ``` - Output is located in `./output/` -- Checkpoint is currently fixed to `checkpoint.xml` also in `./output/` +- Checkpoints as an input have to be explicitely stated in the input ```.xml``` file. In order to verify against the + checkpoint schema, all checkpoint files must point to the valid schema. As this location is hardcoded, all checkpoints + work out of the box if they are in the `/input/` directory. If they are somewhere else, you have to adjust the schema + path in the header of the checkpoint file. + +- A quick example for checkpointing in Assignment 4 task 3: (in `build/src`) + +```bash + ./MolSim -f ../../input/week43checkpoint.xml -c ../../input/ + ..... This eventually writes the checkpoint after running ---------- +``` + +the add the following to week43.xml: + +```xml + + + ../../input/ + + + ... + +``` + +and run + +```bash + ./MolSim -f ../../input/week43.xml +``` + - `--step_size` is relative to the passed simulation time and not the number of iterations - `--loglevel debug` is only available if compiled with CMAKE_BUILD_TYPE=Debug - all other options are specified in the .xml input file - old inputs have been migrated to xml and support this pipeline +## Parallelization strategies + +First, compile with `-DENABLE_OPENMP=ON` to support using OpenMP. + +Then, use the xml tag `` in ``, write true to use the c18 travesal, false to use the force +buffer +method or remove the thing entirely to use no parallelization at all (even if this is active). If OpenMP is not used, +this does nothing. + ## LinkedCells vs DirectSum performance ### Running benchmark.py +Note this is a remnant from week 3 and 4, but this technically could be rewritten to test 5 + For optimal performance run scripts/build with -b for benchmarking The python script uses the generated executable in buildDir/Release/src for execution. Ensure you have python 3.6 or later installed diff --git a/input/checkpoint_membrane_test.xml b/input/checkpoint_membrane_test.xml new file mode 100644 index 00000000..c7525f3a --- /dev/null +++ b/input/checkpoint_membrane_test.xml @@ -0,0 +1,65005 @@ + + + + + + 15 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 15 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 17.199999999999999 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 19.399999999999999 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 21.600000000000001 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 23.800000000000001 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 26 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 28.200000000000003 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 30.400000000000002 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 32.600000000000001 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 34.799999999999997 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 37 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 39.200000000000003 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 41.400000000000006 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 43.600000000000001 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 45.800000000000004 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 48 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 50.200000000000003 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 52.400000000000006 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 54.600000000000001 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 56.800000000000004 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 59 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 61.200000000000003 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 63.400000000000006 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 65.599999999999994 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 67.800000000000011 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 70 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 72.200000000000003 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 74.400000000000006 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 76.600000000000009 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 78.800000000000011 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 81 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 83.200000000000003 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 85.400000000000006 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 87.600000000000009 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 89.800000000000011 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 92 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 94.200000000000003 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 96.400000000000006 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 98.600000000000009 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 100.800000000000011 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 103 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 105.200000000000003 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 107.400000000000006 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 109.600000000000009 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 111.800000000000011 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 114.000000000000014 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 116.200000000000003 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 118.400000000000006 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 15 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 17.199999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 19.399999999999999 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 21.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 23.800000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 26 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 28.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 30.400000000000002 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 32.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 34.799999999999997 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 37 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 39.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 41.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 43.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 45.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 48 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 50.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 52.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 54.600000000000001 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 56.800000000000004 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 59 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 61.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 63.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 65.599999999999994 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 67.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 70 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 72.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 74.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 76.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 78.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 81 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 83.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 85.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 87.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 89.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 92 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 94.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 96.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 98.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 100.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 103 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 105.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 107.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 109.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 111.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 114.000000000000014 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 116.200000000000003 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 118.400000000000006 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 120.600000000000009 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 120.600000000000009 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + + 122.800000000000011 + 122.800000000000011 + 1.5 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 1 + 1 + 1 + + + diff --git a/input/checkpoint_test.xml b/input/checkpoint_test.xml new file mode 100644 index 00000000..c0e22736 --- /dev/null +++ b/input/checkpoint_test.xml @@ -0,0 +1,421 @@ + + + + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 0 + 1.1225 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 0 + 2.245 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 1.1225 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 1.1225 + 1.1225 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 1.1225 + 2.245 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 2.245 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 2.245 + 1.1225 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 2.245 + 2.245 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 0 + 3.3675 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 1.1225 + 3.3675 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 2.245 + 3.3675 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 3.3675 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 3.3675 + 1.1225 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 3.3675 + 2.245 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 3.3675 + 3.3675 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + diff --git a/input/input.xsd b/input/input.xsd index fb0d77f2..62fbefc8 100644 --- a/input/input.xsd +++ b/input/input.xsd @@ -22,6 +22,13 @@ + + + + + + + @@ -30,11 +37,32 @@ + - + + + + + + + + + + + + + + + + + + + + + @@ -52,6 +80,21 @@ + + + + + + + + + + + + + + + @@ -126,6 +169,9 @@ + + + @@ -135,8 +181,29 @@ + + + - + + + + + + + + + + + + + + + + + + + @@ -145,6 +212,7 @@ + \ No newline at end of file diff --git a/input/task53big.xml b/input/task53big.xml new file mode 100644 index 00000000..73aaa31c --- /dev/null +++ b/input/task53big.xml @@ -0,0 +1,98 @@ + + + + + + + 60 + 60 + 60 + + 3.6 + + + + + + + + + + + + + + + + + + + + + + + + + + -12.44 + 1 + + + 0.0005 + 100 + false + + + + + 0 + 0 + 0 + + + 0.6 + 0.6 + 0.6 + + + 50 + 20 + 50 + + 1 + 1.2 + 1 + 1 + 1.2 + 0 + + + + 0 + 0 + 0 + + + 0.6 + 24.6 + 0.6 + + + 50 + 20 + 50 + + 2 + 1.2 + 1 + 1 + 1.1 + 0.0 + + + + 40 + 1000 + + \ No newline at end of file diff --git a/input/week42.xml b/input/week42.xml index 1d319e58..e1268420 100644 --- a/input/week42.xml +++ b/input/week42.xml @@ -34,7 +34,10 @@ - + + -12.44 + 1 + 0.0005 25 diff --git a/input/week42big.xml b/input/week42big.xml index 0af6e727..7f1fcc17 100644 --- a/input/week42big.xml +++ b/input/week42big.xml @@ -34,7 +34,10 @@ - + + -12.44 + 1 + 0.0005 50 diff --git a/input/week43.xml b/input/week43.xml index 59198fbe..cc8a8e9a 100644 --- a/input/week43.xml +++ b/input/week43.xml @@ -34,12 +34,23 @@ - + + -12.44 + 1 + 0.0005 40 true - ../scripts/output/test.xml + + ../scripts/output/test.xml + false + + 0 + 0 + 0 + + diff --git a/input/week43checkpoint.xml b/input/week43checkpoint.xml index 265f9269..38fbcf55 100644 --- a/input/week43checkpoint.xml +++ b/input/week43checkpoint.xml @@ -34,7 +34,10 @@ - + + -12.44 + 1 + 0.0005 15 diff --git a/input/week43checkpointperiodic.xml b/input/week43checkpointperiodic.xml index 9be7947e..998618f8 100644 --- a/input/week43checkpointperiodic.xml +++ b/input/week43checkpointperiodic.xml @@ -34,7 +34,10 @@ - + + -12.44 + 1 + 0.0005 15 diff --git a/input/week43periodic.xml b/input/week43periodic.xml index b3623958..f8a2479f 100644 --- a/input/week43periodic.xml +++ b/input/week43periodic.xml @@ -34,12 +34,23 @@ - + + -12.44 + 1 + 0.0005 40 true - ../scripts/output/test.xml + + ../input/testcheckpointdelete.xml + false + + 0 + 0 + 0 + + diff --git a/input/week51.xml b/input/week51.xml new file mode 100644 index 00000000..e529523a --- /dev/null +++ b/input/week51.xml @@ -0,0 +1,103 @@ + + + + + + + 148 + 148 + 148 + + 4.0 + + + + + + + + + + + + + + + + + + + + + + + + + -0.001 + 2 + + + 2.2 + 300 + + + + 17 + 24 + 0 + + + 17 + 25 + 0 + + + 18 + 24 + 0 + + + 18 + 25 + 0 + + + + 0 + 0 + 0.8 + + + + + 0.01 + 500 + false + + + + + 0 + 0 + 0 + + + 15 + 15 + 1.5 + + + 50 + 50 + 1 + + 1 + 2.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + diff --git a/input/week51ds.xml b/input/week51ds.xml new file mode 100644 index 00000000..0fd0043f --- /dev/null +++ b/input/week51ds.xml @@ -0,0 +1,76 @@ + + + + + + + + + -0.001 + 2 + + + 2.2 + 300 + + + + 17 + 24 + 0 + + + 17 + 25 + 0 + + + 18 + 24 + 0 + + + 18 + 25 + 0 + + + + 0 + 0 + 0.8 + + + + + 0.01 + 500 + false + + + + + 0 + 0 + 0 + + + 15 + 15 + 1.5 + + + 50 + 50 + 1 + + 1 + 2.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + diff --git a/input/week53.xml b/input/week53.xml new file mode 100644 index 00000000..3dbca66c --- /dev/null +++ b/input/week53.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_10_no_walls.xml b/input/week53_10_no_walls.xml new file mode 100644 index 00000000..6ab29bc6 --- /dev/null +++ b/input/week53_10_no_walls.xml @@ -0,0 +1,81 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_11_no_thermostat.xml b/input/week53_11_no_thermostat.xml new file mode 100644 index 00000000..c7ec9f69 --- /dev/null +++ b/input/week53_11_no_thermostat.xml @@ -0,0 +1,122 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + diff --git a/input/week53_12_old_thermostat.xml b/input/week53_12_old_thermostat.xml new file mode 100644 index 00000000..31f83911 --- /dev/null +++ b/input/week53_12_old_thermostat.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + false + + diff --git a/input/week53_13_half_density_fluid.xml b/input/week53_13_half_density_fluid.xml new file mode 100644 index 00000000..150225e8 --- /dev/null +++ b/input/week53_13_half_density_fluid.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 10 + 12 + 5 + + 1 + 2.4 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_14_cuboid_middle.xml b/input/week53_14_cuboid_middle.xml new file mode 100644 index 00000000..dc1aa234 --- /dev/null +++ b/input/week53_14_cuboid_middle.xml @@ -0,0 +1,175 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0 + 0 + 0 + + + 6. + 10. + 0.5 + + + 18 + 5 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 8 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 15 + 0.6 + + + 20 + 13 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_15_left_cuboid.xml b/input/week53_15_left_cuboid.xml new file mode 100644 index 00000000..eb99d28b --- /dev/null +++ b/input/week53_15_left_cuboid.xml @@ -0,0 +1,175 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0 + 0 + 0 + + + 3. + 10.5 + 0.5 + + + 10 + 3 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 8 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 13.8 + 0.6 + + + 20 + 14 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_15_leftright_cuboid.xml b/input/week53_15_leftright_cuboid.xml new file mode 100644 index 00000000..b8ef77fe --- /dev/null +++ b/input/week53_15_leftright_cuboid.xml @@ -0,0 +1,175 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0 + 0 + 0 + + + 3 + 15.5 + 0.5 + + + 10 + 3 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0 + 0 + 0 + + + 16.2 + 20.5 + 0.5 + + + 10 + 3 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_16_leftright_cuboid.xml b/input/week53_16_leftright_cuboid.xml new file mode 100644 index 00000000..54254db4 --- /dev/null +++ b/input/week53_16_leftright_cuboid.xml @@ -0,0 +1,175 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0 + 0 + 0 + + + 35 + 15.55 + 0.55 + + + 10 + 3 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0 + 0 + 0 + + + 16.25 + 20.55 + 0.55 + + + 10 + 3 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_17_lower_sigma.xml b/input/week53_17_lower_sigma.xml new file mode 100644 index 00000000..bb806730 --- /dev/null +++ b/input/week53_17_lower_sigma.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 0.5 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_1_2xg.xml b/input/week53_1_2xg.xml new file mode 100644 index 00000000..2093ad45 --- /dev/null +++ b/input/week53_1_2xg.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -1.6 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_2_4xg.xml b/input/week53_2_4xg.xml new file mode 100644 index 00000000..b7c5e181 --- /dev/null +++ b/input/week53_2_4xg.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -3.2 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_3_8xg.xml b/input/week53_3_8xg.xml new file mode 100644 index 00000000..23fdb0e0 --- /dev/null +++ b/input/week53_3_8xg.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -6.4 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_4_quarterxg.xml b/input/week53_4_quarterxg.xml new file mode 100644 index 00000000..fae336d3 --- /dev/null +++ b/input/week53_4_quarterxg.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.2 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_5_fluid.xml b/input/week53_5_fluid.xml new file mode 100644 index 00000000..6a7204a3 --- /dev/null +++ b/input/week53_5_fluid.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 2.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_5_wall.xml b/input/week53_5_wall.xml new file mode 100644 index 00000000..1311d00a --- /dev/null +++ b/input/week53_5_wall.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 2.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 2.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_6_fluid.xml b/input/week53_6_fluid.xml new file mode 100644 index 00000000..cee5ce57 --- /dev/null +++ b/input/week53_6_fluid.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 4.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_6_wall.xml b/input/week53_6_wall.xml new file mode 100644 index 00000000..ebfbd14b --- /dev/null +++ b/input/week53_6_wall.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 4.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 4.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_7_fluid.xml b/input/week53_7_fluid.xml new file mode 100644 index 00000000..fb53beb5 --- /dev/null +++ b/input/week53_7_fluid.xml @@ -0,0 +1,126 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 0.5 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_7_wall.xml b/input/week53_7_wall.xml new file mode 100644 index 00000000..a1053c43 --- /dev/null +++ b/input/week53_7_wall.xml @@ -0,0 +1,126 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 0.5 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 0.5 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_8_higher_density_fluid.xml b/input/week53_8_higher_density_fluid.xml new file mode 100644 index 00000000..efca326a --- /dev/null +++ b/input/week53_8_higher_density_fluid.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 24 + 30 + 12 + + 1 + 1.0 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week53_9_one_sided_fluid.xml b/input/week53_9_one_sided_fluid.xml new file mode 100644 index 00000000..a47bac42 --- /dev/null +++ b/input/week53_9_one_sided_fluid.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 10 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week54_cm1_higherdensityfluid.xml b/input/week54_cm1_higherdensityfluid.xml new file mode 100644 index 00000000..4fd837bb --- /dev/null +++ b/input/week54_cm1_higherdensityfluid.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 24 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 15 + 15 + 200 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 24 + 30 + 24 + + 1 + 0.8 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week54_cm2_lowersigmafluid.xml b/input/week54_cm2_lowersigmafluid.xml new file mode 100644 index 00000000..19a02735 --- /dev/null +++ b/input/week54_cm2_lowersigmafluid.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 24 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 15 + 15 + 200 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 20 + + 1 + 1.2 + 1.0 + 1.0 + 0.5 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week54_cm3_cuboidmiddle.xml b/input/week54_cm3_cuboidmiddle.xml new file mode 100644 index 00000000..16b2fe23 --- /dev/null +++ b/input/week54_cm3_cuboidmiddle.xml @@ -0,0 +1,176 @@ + + + + + + + 30 + 30 + 24 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 15 + 15 + 200 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0 + 0 + 0 + + + 6.05 + 10.05 + 0.55 + + + 18 + 5 + 24 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 8 + 20 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 15 + 0.6 + + + 20 + 13 + 20 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + + 40 + 10 + true + + diff --git a/input/week54_cm4_leftcuboid.xml b/input/week54_cm4_leftcuboid.xml new file mode 100644 index 00000000..90709df6 --- /dev/null +++ b/input/week54_cm4_leftcuboid.xml @@ -0,0 +1,175 @@ + + + + + + + 30 + 30 + 24 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 15 + 15 + 200 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0 + 0 + 0 + + + 3. + 10.5 + 0.5 + + + 10 + 3 + 24 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 8 + 20 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 13.8 + 0.6 + + + 20 + 14 + 20 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week54_cm5_nothermostat.xml b/input/week54_cm5_nothermostat.xml new file mode 100644 index 00000000..f2356ba4 --- /dev/null +++ b/input/week54_cm5_nothermostat.xml @@ -0,0 +1,122 @@ + + + + + + + 30 + 30 + 24 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 15 + 15 + 200 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 20 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + diff --git a/input/week54_cm6_4xmasswall.xml b/input/week54_cm6_4xmasswall.xml new file mode 100644 index 00000000..b29682fd --- /dev/null +++ b/input/week54_cm6_4xmasswall.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 24 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 15 + 15 + 200 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -1 + 1.0 + 4.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -2 + 1.0 + 4.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 20 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week54_cm7_4xmassfluid.xml b/input/week54_cm7_4xmassfluid.xml new file mode 100644 index 00000000..ac7d50f5 --- /dev/null +++ b/input/week54_cm7_4xmassfluid.xml @@ -0,0 +1,127 @@ + + + + + + + 30 + 30 + 24 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + + 0.0005 + 500 + false + + 15 + 15 + 200 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 24 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 20 + + 1 + 1.2 + 4.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week54_small_profiling.xml b/input/week54_small_profiling.xml new file mode 100644 index 00000000..748b5017 --- /dev/null +++ b/input/week54_small_profiling.xml @@ -0,0 +1,126 @@ + + + + + + + 30 + 30 + 12 + + 2.75 + + + + + + + + + + + + + + + + + + + + + + + + + + -0.8 + 1 + + 0.0005 + 10 + false + + 50 + 1 + 10000 + + + + + + 0 + 0 + 0 + + + 1.0 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -1 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 27.2 + 0.5 + 0.5 + + + 2 + 30 + 12 + + -2 + 1.0 + 1.0 + 2.0 + 1.1 + 0.0 + + + + 0.0 + 0.0 + 0.0 + + + 3.2 + 0.6 + 0.6 + + + 20 + 25 + 10 + + 1 + 1.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + + 40 + 10 + true + + diff --git a/input/week5_actual3.xml b/input/week5_actual3.xml new file mode 100644 index 00000000..e1147cdd --- /dev/null +++ b/input/week5_actual3.xml @@ -0,0 +1,104 @@ + + + + + + + 60 + 60 + 60 + + 3.6 + + + + + + + + + + + + + + + + + + + + + + + + + + + -12.44 + 1 + + + 0.0005 + 100 + false + + 1 + 1 + 100000000 + + + + + + 0.0 + 0.0 + 0.0 + + + 0.6 + 0.6 + 0.6 + + + 50 + 20 + 50 + + 1 + 1.2 + 1.0 + 1.0 + 1.2 + 0.0 + + + + 0 + 0 + 0 + + + 0.6 + 24.6 + 0.6 + + + 50 + 20 + 50 + + 2 + 1.2 + 2.0 + 1.0 + 1.1 + 0.1 + + + + 40 + 1000 + + diff --git a/scripts/build b/scripts/build index 8a16f2ea..49d89d77 100755 --- a/scripts/build +++ b/scripts/build @@ -1,7 +1,7 @@ #!/bin/bash help() { - echo "builds the project in /buildDir/ and exports the path to the executable in \$BUILD" + echo "builds the project in /build and exports the path to the executable in \$BUILD" printf "predefined s are:\t Release (default)\n" printf "\t\t\t\t\t Debug\n" printf "\t\t\t\t\t asan\n" @@ -14,6 +14,7 @@ help() { RELEASE_TYPE="Release" RUN_TESTS="OFF" ENABLE_BENCHMARK="OFF" +ENABLE_OPENMP="OFF" # parse args for arg in "$@"; do @@ -29,6 +30,10 @@ for arg in "$@"; do then ENABLE_BENCHMARK="ON" echo "enabled benchmarking" + elif [[ $arg == "-p" || $arg == "--parallel" ]] + then + ENABLE_OPENMP="ON" + echo "enabled omp" else RELEASE_TYPE="$arg" printf "changed Release Type to %s\n" "$RELEASE_TYPE" @@ -38,12 +43,13 @@ done printf "\033[32mbuilding '%s'\033[0m\n" "$RELEASE_TYPE" # create buildDir if needed -if [ -d "../buildDir/$RELEASE_TYPE" ] +BUILD_DIR="../build${RELEASE_TYPE}" +if [ -d "$BUILD_DIR" ] then - printf "\033[32mdetected existing 'buildDir/%s'\033[0m\n" "$RELEASE_TYPE" + printf "\033[32mdetected existing 'build%s'\033[0m\n" "$RELEASE_TYPE" else - mkdir -p ../buildDir/"$RELEASE_TYPE" - echo "created 'buildDir/$RELEASE_TYPE'" + mkdir -p "$BUILD_DIR" + echo "created 'build${RELEASE_TYPE}'" fi echo "" @@ -52,9 +58,9 @@ echo "" # setup cmake start_time=$(date +%s%N) -cd ../buildDir/"$RELEASE_TYPE" || exit 1 -cmake ../../CMakeLists.txt -cmake -DCMAKE_BUILD_TYPE="$RELEASE_TYPE" -DBUILD_TESTS="$RUN_TESTS" -DENABLE_BENCHMARK="$ENABLE_BENCHMARK" . +cd "$BUILD_DIR" || exit 1 +cmake ../CMakeLists.txt +cmake -DCMAKE_BUILD_TYPE="$RELEASE_TYPE" -DBUILD_TESTS="$RUN_TESTS" -DENABLE_BENCHMARK="$ENABLE_BENCHMARK" -DENABLE_OPENMP="$ENABLE_OPENMP" . echo "" printf "\033[32m--- BUILDING: ---\033[0m\n" @@ -63,7 +69,7 @@ echo "" # build with cmake and all cores if ! cmake --build . -- -"j$(nproc)"; then printf "\033[31mbuild failed!\033[0m\n" - cd ../../scripts || return 127 # navigate back + cd ../scripts || return 127 # navigate back return 127 fi @@ -78,7 +84,7 @@ then echo "" if ! ctest; then printf "\033[31mtests failed!\033[0m\n" - cd ../../../scripts || return 127 # navigate back + cd ../../scripts || return 127 # navigate back return 127 fi cd .. @@ -112,7 +118,7 @@ BUILD_FILE=$(dirname "$(realpath "$0")")"/src/MolSim" export BUILD="$BUILD_FILE" # navigate back since this is should have been executed with the same shell -cd ../../scripts || return 127 +cd ../scripts || return 127 echo "" printf "\033[32mpath to executable is stored in \$BUILD:\033[0m\n" diff --git a/scripts/heatmap.py b/scripts/heatmap.py new file mode 100644 index 00000000..a0e26f4a --- /dev/null +++ b/scripts/heatmap.py @@ -0,0 +1,75 @@ +import sys + +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +import seaborn as sns +import os +from datetime import datetime + +# Load CSV data +csv_file = "/home/maximilian/Downloads/OneDrive_2025-01-26/task 4/task4_quarterxg/density.csv" # Replace with your CSV file path +output_directory = "./output/heatmap_" + datetime.now().strftime("%Y-%m-%d_%H-%M-%S") +os.makedirs(output_directory) + +if len(sys.argv) > 1: + csv_file = sys.argv[1] + print("using cli csv file") + +data = pd.read_csv(csv_file) + +# Ensure the CSV has the expected columns +if len(data.columns) < 51: + raise ValueError("The CSV file must have at least 51 columns: 'time' and 50 data columns.") + +# Extract time and data columns +time = data.iloc[:, 0] # First column is time +data_columns = data.iloc[:, 1:51] # Extract the next 50 columns + +# Convert to NumPy array for plotting +data_array = data_columns.to_numpy() + +number_of_heatmaps = data_columns.shape[0] + +global_min = np.min(data_array) +global_max = np.max(data_array) + +# Generate a heatmap for each line in the CSV +for i, row in enumerate(data_array): + current_min = row.min(axis=0) + current_avg = row.mean(axis=0) + current_max = row.max(axis=0) + + plt.figure(figsize=(6, 6)) # Adjust figure size for a full heatmap + ax = sns.heatmap( + row.reshape(1, -1), # Reshape row into a 2D array with one row + cmap="summer", # Color scheme + cbar=True, # Display color bar + cbar_kws={'label': 'density in p/u³'}, # Add label to the color bar + vmin = global_min, # Set global minimum for color scale + vmax = global_max, # Set global maximum for color scale + xticklabels=False, # Disable x-axis tick labels + yticklabels=False, # Disable y-axis tick labels + linewidths=0 + ) + + # Add labels and titles + ax.set_title(f"Density @ t = {time[i]:06.2f}", fontsize=16, fontweight="bold") + ax.text(5, 1.05, f"min: {current_min:04.2f}, avg: {current_avg:04.2f}, max: {current_max:04.2f}") + + ax.set_xlabel("") + ax.set_ylabel("") + + # Display the plot + plt.tight_layout() + #plt.show() + plt.savefig(output_directory + f"/heatmap_{time[i]}.svg", format='svg') + plt.close() + + percent = 100 * (i / float(number_of_heatmaps)) + filled_length = int(20 * i // number_of_heatmaps) + bar = '█' * filled_length + '-' * (20 - filled_length) + sys.stdout.write(f'\r|{bar}| {percent:.1f}% Complete') + sys.stdout.flush() + if i == number_of_heatmaps: + print() # Move to the next line when done. diff --git a/scripts/heatmap_extended.py b/scripts/heatmap_extended.py new file mode 100644 index 00000000..99be5b48 --- /dev/null +++ b/scripts/heatmap_extended.py @@ -0,0 +1,76 @@ +import sys + +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +import seaborn as sns +import os +from datetime import datetime + +# Load CSV data +csv_file = "/home/maximilian/CLionProjects/MolSim/scripts/output/2025-01-25 16:40:42/density.csv" # Replace with your CSV file path +output_directory = "./output/heatmap_extended_" + datetime.now().strftime("%Y-%m-%d_%H-%M-%S") +os.makedirs(output_directory) + +if len(sys.argv) > 1: + csv_file = sys.argv[1] + print("using cli csv file") + +data = pd.read_csv(csv_file) + +# Ensure the CSV has the expected columns +if len(data.columns) < 226: + raise ValueError("The CSV file must have at least 225 columns: 'time' and 225 data columns.") + +# Extract time and data columns +time = data.iloc[:, 0] # First column is time +data_columns = data.iloc[:, 1:256] # Extract the next 50 columns + +# Convert to NumPy array for plotting +data_array = data_columns.to_numpy() + +number_of_heatmaps = data_columns.shape[0] + +global_min = np.min(data_array) +global_max = np.max(data_array) + +# Generate a heatmap for each line in the CSV +for i, row in enumerate(data_array): + current_min = row.min(axis=0) + current_avg = row.mean(axis=0) + current_max = row.max(axis=0) + + + plt.figure(figsize=(5, 5)) # Adjust figure size for a full heatmap + ax = sns.heatmap( + row.reshape(15, 15)[::-1], + cmap="summer", # Color scheme + cbar=True, # Display color bar + cbar_kws={'label': 'density in p/u³'}, # Add label to the color bar + vmin = global_min, # Set global minimum for color scale + vmax = global_max, # Set global maximum for color scale + xticklabels=False, # Disable x-axis tick labels + yticklabels=False, # Disable y-axis tick labels + linewidths=0 + ) + + # Add labels and titles + ax.set_title(f"Density @ t = {time[i]:06.2f}", fontsize=16, fontweight="bold") + ax.text(3, 0.7, f"min: {current_min:04.2f}, avg: {current_avg:04.2f}, max: {current_max:04.2f}") + + ax.set_xlabel("") + ax.set_ylabel("") + + # Display the plot + plt.tight_layout() + #plt.show() + plt.savefig(output_directory + f"/heatmap_{time[i]}.svg", format='svg') + plt.close() + + percent = 100 * (i / float(number_of_heatmaps)) + filled_length = int(20 * i // number_of_heatmaps) + bar = '█' * filled_length + '-' * (20 - filled_length) + sys.stdout.write(f'\r|{bar}| {percent:.1f}% Complete') + sys.stdout.flush() + if i == number_of_heatmaps: + print() # Move to the next line when done. diff --git a/scripts/runtime b/scripts/runtime new file mode 100755 index 00000000..5497b3d8 --- /dev/null +++ b/scripts/runtime @@ -0,0 +1,27 @@ +#!/bin/bash + +# Check if a command is provided as an argument +if [ $# -eq 0 ]; then + echo "Usage: $0 " + exit 1 +fi + +command=$@ +total_time=0 + +# Execute the command three times +for i in {1..3}; do + echo "Run $i: Executing '$command'..." + start_time=$(date +%s.%N) # Record start time + eval "$command" >/dev/null 2>&1 # Execute the command, suppress output + end_time=$(date +%s.%N) # Record end time + + # Calculate the elapsed time for this run + elapsed_time=$(echo "$end_time - $start_time" | bc) + echo "Run $i: Execution time = ${elapsed_time}s" + total_time=$(echo "$total_time + $elapsed_time" | bc) +done + +# Calculate and print the average execution time +average_time=$(echo "$total_time / 3" | bc -l) +printf "Average execution time: %.6fs\n" "$average_time" diff --git a/slides/assignment_1_slides.pdf b/slides/assignment_1_slides.pdf new file mode 100644 index 00000000..7f5e5745 Binary files /dev/null and b/slides/assignment_1_slides.pdf differ diff --git a/slides/assignment_2_slides.pdf b/slides/assignment_2_slides.pdf new file mode 100644 index 00000000..8ecea5f3 Binary files /dev/null and b/slides/assignment_2_slides.pdf differ diff --git a/slides/assignment_3_slides.pdf b/slides/assignment_3_slides.pdf new file mode 100644 index 00000000..b29594e4 Binary files /dev/null and b/slides/assignment_3_slides.pdf differ diff --git a/slides/assignment_4_slides.pdf b/slides/assignment_4_slides.pdf new file mode 100644 index 00000000..b7f47cb9 Binary files /dev/null and b/slides/assignment_4_slides.pdf differ diff --git a/slides/assignment_5_slides.pdf b/slides/assignment_5_slides.pdf new file mode 100644 index 00000000..1de03298 Binary files /dev/null and b/slides/assignment_5_slides.pdf differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4cf5c0a9..a9f6290e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,11 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +option(ENABLE_OPENMP "Enable OpenMP support" OFF) + +if (ENABLE_OPENMP) + find_package(OpenMP REQUIRED) +endif () + ##### Collect all cpp files except MolSim.cpp ##### file(GLOB_RECURSE MY_SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" @@ -11,14 +17,26 @@ list(REMOVE_ITEM MY_SRC "${CMAKE_CURRENT_SOURCE_DIR}/MolSim.cpp") add_library(MolSimLib ${MY_SRC} + forces/HarmonicForce.cpp + forces/HarmonicForce.h + forces/TruncatedLennardJones.cpp + forces/TruncatedLennardJones.h + forces/IndexForce.cpp + forces/IndexForce.h ) + target_link_libraries(MolSimLib PUBLIC - xerces-c spdlog::spdlog ) +if (ENABLE_OPENMP AND OpenMP_CXX_FOUND) + target_link_libraries(MolSimLib PUBLIC OpenMP::OpenMP_CXX) +endif () + +target_link_libraries(MolSimLib PRIVATE xerces-c) + target_compile_features(MolSimLib PUBLIC cxx_std_17 ) @@ -44,6 +62,7 @@ add_executable(MolSim MolSim.cpp io/file/out/checkpoint-schema.hxx io/file/out/checkpoint-schema.cxx io/file/out/XmlWriter.cpp + defs/Generators/MembraneGenerator.cpp ) set(FILES_TO_DISABLE_WARNINGS @@ -56,6 +75,11 @@ set_source_files_properties(${FILES_TO_DISABLE_WARNINGS} COMPILE_OPTIONS "-w" ) + +if (ENABLE_OPENMP AND OpenMP_CXX_FOUND) + target_link_libraries(MolSimLib PUBLIC OpenMP::OpenMP_CXX) +endif () + target_link_libraries(MolSim PUBLIC MolSimLib) target_compile_options(MolSimLib @@ -66,3 +90,5 @@ target_compile_options(MolSimLib $<$:-w3 -wd383,981,1418,1572,2259> ) + + diff --git a/src/MolSim.cpp b/src/MolSim.cpp index 82d29fab..1e9aa0ae 100644 --- a/src/MolSim.cpp +++ b/src/MolSim.cpp @@ -2,52 +2,29 @@ #include #include "calc/VerletIntegrator.h" +#include "debug/debug_print.h" #include "defs/Thermostat.h" #include "defs/containers/DirectSumContainer.h" #include "defs/containers/LinkedCellsContainer.h" #include "forces/Gravity.h" +#include "forces/HarmonicForce.h" +#include "forces/IndexForce.h" #include "forces/LennardJones.h" #include "forces/SingularGravity.h" +#include "forces/TruncatedLennardJones.h" #include "io/CLArgumentParser.h" #include "io/file/in/xml/XmlReader.h" #include "io/file/out/OutputHelper.h" #include "io/file/out/VTKWriter.h" #include "io/file/out/XmlWriter.h" #include "spdlog/stopwatch.h" -#include "utils/ArrayUtils.h" #include "utils/SpdWrapper.h" +#include "utils/Statistics.h" int main(const int argc, char* argv[]) { -#ifndef BENCHMARK - SpdWrapper::get()->info("Application started"); -#endif - Arguments arguments = { - .t_end = 5, - .delta_t = 0.0002, - .force_type = Arguments::LennardJones, - .thermostat_config = - { - .T_init = 0.5, - .T_target = 0.5, - .deltaT = 0.5, - .n_thermostat = 1000, - .use_relative = false, - }, - .container_data = - LinkedCellsConfig{.domain = {100, 100, 100}, - .cutoff_radius = 3.0, - .boundary_config = - { - .x_high = LinkedCellsConfig::Outflow, - .x_low = LinkedCellsConfig::Outflow, - .y_high = LinkedCellsConfig::Outflow, - .y_low = LinkedCellsConfig::Outflow, - .z_high = LinkedCellsConfig::Outflow, - .z_low = LinkedCellsConfig::Outflow, - }}, - - }; - auto [input_file, step_size, write_checkpoint] = + Arguments arguments = {}; + + auto [input_file, step_size, checkpoint_path] = CLArgumentParser::parse(argc, argv); std::vector particles; @@ -57,12 +34,23 @@ int main(const int argc, char* argv[]) { printConfiguration(arguments); SpdWrapper::get()->info("Step size: {}", step_size); + std::vector> index_forces; + for (const auto& config : arguments.index_force_configs) { + const auto& [coords, ids, time, force_values] = config; + SpdWrapper::get()->info("Adding index force with indices {}", ids.size()); + index_forces.push_back( + std::make_unique(ids, time, force_values)); + } + std::unique_ptr container; if (std::holds_alternative(arguments.container_data)) { - const auto& linked_cells_data = + auto& linked_cells_data = std::get(arguments.container_data); + container = std::make_unique(linked_cells_data); + container->addParticles(particles); + container->imposeInvariant(); } else if (std::holds_alternative( arguments.container_data)) { @@ -72,76 +60,105 @@ int main(const int argc, char* argv[]) { SpdWrapper::get()->error("Unrecognized container type"); throw std::runtime_error("Unrecognized container type"); } - std::cout << particles.size() << " particles" << std::endl; + INFO_FMT("Simulation is using {} particles", particles.size()); + + // assign all forces from the configs + std::vector> interactive_forces; + + std::vector> singular_forces; + + // Assign interactive forces for (auto& config : arguments.interactive_force_types) { if (std::holds_alternative(config)) { interactive_forces.push_back(std::make_unique()); } else if (std::holds_alternative(config)) { interactive_forces.push_back(std::make_unique()); + } else if (std::holds_alternative(config)) { + interactive_forces.push_back(std::make_unique()); } else { SpdWrapper::get()->error("Unrecognized interactive_force_type"); } } - std::vector> singular_forces; + // Assign singular forces + for (auto config : arguments.singular_force_types) { if (std::holds_alternative(config)) { - const auto& [g] = std::get(config); - singular_forces.push_back( - std::move(std::make_unique(g))); + const auto& [g, axis] = std::get(config); + singular_forces.push_back(std::make_unique(g, axis)); + } else if (std::holds_alternative(config)) { + const auto& [r, k] = std::get(config); + singular_forces.push_back(std::make_unique(k, r)); + } else { SpdWrapper::get()->error("Unrecognized singular force"); } } VerletIntegrator verlet_integrator(interactive_forces, singular_forces, - arguments.delta_t); + index_forces, arguments.delta_t, + arguments.strategy); outputWriter::VTKWriter writer; std::unique_ptr thermostat; - const std::string outputDirectory = - createOutputDirectory("./output/", argc, argv); if (arguments.use_thermostat) { thermostat = std::make_unique(arguments.thermostat_config); } + const std::string output_directory = + createOutputDirectory("./output/", argc, argv); + double current_time = 0; int iteration = 0; auto number_of_particles = particles.size(); + const auto start_time = std::chrono::high_resolution_clock::now(); + size_t particle_updates = 0; + #ifndef BENCHMARK int writes = 0; int percentage = 0; double next_output_time = 0; spdlog::stopwatch stopwatch; - // it is unfeasible to check the numbers of outflown particles every - // iteration, so it is assumed that the number of particles is constant - auto iteration_of_last_mups = 0; -#endif - const auto start_time = std::chrono::high_resolution_clock::now(); auto time_of_last_mups = start_time; + + Statistics statistics( + arguments.statistics_config.x_bins, arguments.statistics_config.y_bins, + *container, + output_directory + arguments.statistics_config.density_output_location, + output_directory + arguments.statistics_config.velocity_output_location); +#endif + while (current_time <= arguments.t_end) { verlet_integrator.step(*container); if (arguments.use_thermostat) { - if (iteration % thermostat->n_thermostat == 0 && iteration > 0) { + if (iteration % thermostat->getNThermostat() == 0 && iteration > 0) { thermostat->setTemperature(*container); } } + + if (iteration % 100 == 0) { + SpdWrapper::get()->info("Iteration {}", iteration); + } + #ifdef BENCHMARK // these are the first 1000 iterations for the contest if (iteration == 1000) { - const auto first_1k = std::chrono::high_resolution_clock::now(); - const std::chrono::duration elapsed = first_1k - start_time; - std::cout << "First 1k iterations took: " << elapsed.count() << " seconds" - << std::endl; + const auto first_1_k = std::chrono::high_resolution_clock::now(); + const std::chrono::duration elapsed = first_1_k - start_time; + std::cout << "First 10k iterations took: " << elapsed.count() + << " seconds" << std::endl; const auto mups = static_cast(number_of_particles) * 1000 * (1.0 / elapsed.count()); - std::cout << "MMUPS for first 1k iterations: " << mups * (1.0 / 1e6) + std::cout << "MMUPS for first 10k iterations: " << mups * (1.0 / 1e6) << std::endl; + exit(0); } #endif + particle_updates += container->getParticleCount(); + #ifndef BENCHMARK if (current_time >= next_output_time) { - plotParticles(outputDirectory, iteration, writer, *container); + plotParticles(output_directory, iteration, writer, *container); writes++; next_output_time = writes * step_size; // check if next percentage complete @@ -166,11 +183,11 @@ int main(const int argc, char* argv[]) { std::chrono::duration_cast( current_time_hrc - time_of_last_mups) .count(); - double mmups = (iteration - iteration_of_last_mups) * - static_cast(number_of_particles) * - (1.0 / static_cast(microseconds)); - iteration_of_last_mups = iteration; + + double mmups = + particle_updates * (1.0 / static_cast(microseconds)); time_of_last_mups = current_time_hrc; + particle_updates = 0; // mmups are unaccounted for write time, therefore it is always a lower // bound @@ -182,14 +199,22 @@ int main(const int argc, char* argv[]) { percentage++; } } + + if (arguments.statistics_config.calc_stats && + iteration % arguments.statistics_config.output_interval == 0) { + statistics.writeStatistics(current_time); + } #endif iteration++; current_time = arguments.delta_t * iteration; } // Writes the finished simulations particle state into a checkpoint file - if (write_checkpoint) { - XmlWriter::writeFile(*container, "./output/test.xml"); + // if (write_checkpoint) { + // XmlWriter::writeFile(*container, "./output/test.xml"); + //} + if (checkpoint_path) { + XmlWriter::writeFile(*container, checkpoint_path.value()); } #ifdef BENCHMARK @@ -205,6 +230,10 @@ int main(const int argc, char* argv[]) { double mmups = iteration * static_cast(number_of_particles) * (1.0 / static_cast(microseconds)); std::cout << "MMUPS: " << mmups << std::endl; + +#ifndef BENCHMARK + statistics.closeFiles(); +#endif SpdWrapper::get()->info("Output written. Terminating..."); return 0; diff --git a/src/calc/Integrator.h b/src/calc/Integrator.h deleted file mode 100644 index 4aa8929f..00000000 --- a/src/calc/Integrator.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#include -#include - -#include "defs/containers/ParticleContainer.h" -#include "forces/InteractiveForce.h" -#include "forces/SingularForce.h" - -/** - * @brief Interface for different types of integrators - */ -class Integrator { - protected: - std::vector> interactive_forces; - std::vector> singular_forces; - double delta_t; - - public: - /** - * @brief Create Integrator object - * @param interactive_forces References to the type of force applied each - * iteration - * @param singular_forces singular forces acting on single particles globally - * @param delta_t Delta time - * @note Since this is an interface, it's invalid - */ - Integrator(std::vector>& interactive_forces, - std::vector>& singular_forces, - const double delta_t) - : interactive_forces(std::move(interactive_forces)), - singular_forces(std::move(singular_forces)), - delta_t(delta_t){}; - - /** - * @brief Virtual destructor for all Integrator inheritors - */ - virtual ~Integrator() = default; - - /** - * @brief Virtual method to advance time by one step - * @param particle_container - */ - virtual void step(ParticleContainer& particle_container) = 0; -}; diff --git a/src/calc/VerletIntegrator.cpp b/src/calc/VerletIntegrator.cpp index 6105efba..722c44c8 100644 --- a/src/calc/VerletIntegrator.cpp +++ b/src/calc/VerletIntegrator.cpp @@ -4,38 +4,74 @@ #include "VerletIntegrator.h" #include "../utils/ArrayUtils.h" +#include "utils/SpdWrapper.h" void VerletIntegrator::step(ParticleContainer& particle_container) { + // position update particle_container.singleIterator([this](Particle& p) { - const dvec3 new_x = p.getX() + delta_t * p.getV() + - (delta_t * delta_t / (2 * p.getM())) * (p.getF()); + if (p.getType() < 0) { + return; + } // ignore position update for walls + const dvec3 new_x = p.getX() + delta_t_ * p.getV() + + (delta_t_ * delta_t_ / (2 * p.getM())) * (p.getF()); + p.setX(new_x); }); + // reset particle_container.singleIterator([](Particle& p) { p.updateForceInTime(); }); particle_container.imposeInvariant(); particle_container.singleIterator([this](Particle& p) { dvec3 f = {0, 0, 0}; - for (const auto& force : singular_forces) { - f = f + force->applyForce(p); + for (const auto& index_force : index_forces_) { + for (const int id : index_force->getIndices()) { + if (p.getId() == id) { + f = f + index_force->applyForce(p, current_time_); + } + } } p.addF(f); }); - particle_container.pairIterator([this](Particle& p1, Particle& p2) { - dvec3 f12 = {0.0, 0.0, 0.0}; - for (const auto& force : interactive_forces) { - f12 = f12 + force->directionalForce(p1, p2); +#ifdef _OPENMP + if (strategy_ == ParallelStrategy::STRATEGY_2) { + particle_container.computeInteractiveForcesC18(interactive_forces_); + } else if (strategy_ == ParallelStrategy::STRATEGY_1) { + particle_container.computeInteractiveForcesForceBuffer(interactive_forces_); + } else { + particle_container.pairIterator([this](Particle& p, Particle& q) { + dvec3 f = {0, 0, 0}; + for (const auto& interactive_force : interactive_forces_) { + f = f + interactive_force->directionalForce(p, q); + } + p.addF(f); + q.subF(f); + }); + } +#else + particle_container.pairIterator([this](Particle& p, Particle& q) { + dvec3 f = {0, 0, 0}; + for (const auto& interactive_force : interactive_forces_) { + f = f + interactive_force->directionalForce(p, q); } - p1.addF(f12); - p2.subF(f12); + p.addF(f); + q.subF(f); }); +#endif + particle_container.computeSingularForces(singular_forces_); + + // Velocity Update particle_container.singleIterator([this](Particle& p) { + if (p.getType() < 0) { + return; // ignore velocity update for walls, theoretically + } const dvec3 new_v = - p.getV() + (delta_t / (2 * p.getM()) * (p.getOldF() + p.getF())); + p.getV() + (delta_t_ / (2 * p.getM()) * (p.getOldF() + p.getF())); p.setV(new_v); }); + + incrementTime(); } diff --git a/src/calc/VerletIntegrator.h b/src/calc/VerletIntegrator.h index bfb6801d..bfa7a860 100644 --- a/src/calc/VerletIntegrator.h +++ b/src/calc/VerletIntegrator.h @@ -1,46 +1,102 @@ -// -// Created by jkr on 10/18/24. -// - -#ifndef VERLET_H -#define VERLET_H #pragma once -#include -#include "Integrator.h" +#ifndef VERLETINTEGRATOR_H +#define VERLETINTEGRATOR_H + +#include +#include + +#include "defs/Simulation.h" +#include "forces/IndexForce.h" +#include "forces/InteractiveForce.h" +#include "forces/SingularForce.h" /** - * @brief Integrator using the Verlet-integration method on a particle - * container. + * @brief Interface for different types of integrators */ -class VerletIntegrator final : public Integrator { +class VerletIntegrator { + protected: + /** + * Vector of applied interactive forces + */ + std::vector> interactive_forces_; + /** + * Vector of applied singular forces + */ + std::vector> singular_forces_; + /** + * Vector of applied index forces + */ + std::vector> index_forces_; + /** + * Time step + */ + double delta_t_; + /** + * Current time for index force + */ + double current_time_; + /** + * Parallelisation strategy used, default is 3 so no parallelisation + */ + ParallelStrategy strategy_ = ParallelStrategy::STRATEGY_3; + public: /** - * @brief Create VerletIntegrator object - * @param interactive_forces Reference to the type of force applied each - * @param singular_forces Global forces acting on each particle as is + * @brief Create Integrator object + * @param interactive_forces References to the type of force applied each + * iteration + * @param singular_forces singular forces acting on single particles globally * @param delta_t Delta time + * @param strategy whether to use C18 or force buffer + * @param index_forces Index forces applied each step + * @note Since this is an interface, it's invalid */ VerletIntegrator( std::vector>& interactive_forces, std::vector>& singular_forces, + std::vector>& index_forces, + const double delta_t, const ParallelStrategy strategy) + : interactive_forces_(std::move(interactive_forces)), + singular_forces_(std::move(singular_forces)), + index_forces_(std::move(index_forces)), + delta_t_(delta_t), + current_time_(0), + strategy_(strategy){}; + + /** + * @brief Create Integrator object with Strategy 3 (no OpenMP) + * @param interactive_forces References to the type of force applied each + * iteration + * @param singular_forces singular forces acting on single particles globally + * @param index_forces Index forces applied each step + * @param delta_t Delta time + */ + explicit VerletIntegrator( + std::vector>& interactive_forces, + std::vector>& singular_forces, + std::vector>& index_forces, const double delta_t) - : Integrator(interactive_forces, singular_forces, delta_t) {} + : interactive_forces_(std::move(interactive_forces)), + singular_forces_(std::move(singular_forces)), + index_forces_(std::move(index_forces)), + delta_t_(delta_t), + current_time_(0){}; + + /** + * @brief Virtual destructor for all Integrator inheritors + */ + ~VerletIntegrator() = default; /** - * @brief Destructor + * @brief Virtual method to advance time by one step + * @param particle_container */ - ~VerletIntegrator() override = default; + void step(ParticleContainer& particle_container); /** - * @brief Advance particle-system by one time-step. - * @note Update order: \n - * 1) Particle positions and boundary conditions \n - * 2) Intra particular forces \n - * 3) Particle velocities \n - * - * @param particle_container Reference to the current particle-system + * @brief increments the current simulation time by delta_t_ */ - void step(ParticleContainer& particle_container) override; + void incrementTime() { current_time_ += delta_t_; } }; -#endif // VERLET_H +#endif \ No newline at end of file diff --git a/src/debug/debug_print.h b/src/debug/debug_print.h index 23a93af5..e6cfc2c8 100644 --- a/src/debug/debug_print.h +++ b/src/debug/debug_print.h @@ -6,8 +6,9 @@ #ifndef DEBUG_H #define DEBUG_H +#ifdef DEBUG #include // Also careful here, when clion is not in debug mode it tells you to remove this - +#endif #include "utils/SpdWrapper.h" #ifdef DEBUG @@ -18,4 +19,18 @@ #define DEBUG_PRINT_FMT(msg, ...) #endif +#define INFO(msg) SpdWrapper::get()->info(msg); +#define INFO_FMT(msg, ...) SpdWrapper::get()->info(msg, __VA_ARGS__); + +/** + * @brief Info prints any vector of type T and length 3 + * @tparam T any type that's in vector (double and int here) + * @param msg The message in front of the vector, or the debug information + * @param vec the vector to be printed + */ +template +void InfoVec(std::string msg, std::array vec) { + INFO_FMT("{} -- [{},{},{}]", msg, vec[0], vec[1], vec[2]); +} + #endif // DEBUG_H diff --git a/src/defs/Generators/CuboidGenerator.cpp b/src/defs/Generators/CuboidGenerator.cpp index f241f9a6..ab8c6761 100644 --- a/src/defs/Generators/CuboidGenerator.cpp +++ b/src/defs/Generators/CuboidGenerator.cpp @@ -11,29 +11,29 @@ CuboidGenerator::CuboidGenerator(const dvec3 &corner, const std::array &dimensions, const double h, const double m, - const std::array &initialVelocity, + const std::array &initial_velocity, const double mv, const double epsilon, const double sigma, const int type, - const bool twoD) - : corner(corner), - dimensions(dimensions), - h(h), - m(m), - initialVelocity(initialVelocity), - mv(mv), - epsilon(epsilon), - sigma(sigma), - type(type), - twoD(twoD) { + const bool two_d) + : corner_(corner), + dimensions_(dimensions), + h_(h), + m_(m), + initial_velocity_(initial_velocity), + mv_(mv), + epsilon_(epsilon), + sigma_(sigma), + type_(type), + two_d_(two_d) { DEBUG_PRINT_FMT("CuboidGenerator of dim {} created with parameters:", - twoD ? 2 : 3); + two_d ? 2 : 3); DEBUG_PRINT_FMT("corner: ({}, {}, {})", corner[0], corner[1], corner[2]); DEBUG_PRINT_FMT("dimensions: ({}, {}, {})", dimensions[0], dimensions[1], dimensions[2]); DEBUG_PRINT_FMT("h: {}", h); DEBUG_PRINT_FMT("m: {}", m); - DEBUG_PRINT_FMT("initialVelocity: ({}, {}, {})", initialVelocity[0], - initialVelocity[1], initialVelocity[2]); + DEBUG_PRINT_FMT("initialVelocity: ({}, {}, {})", initial_velocity_[0], + initial_velocity_[1], initial_velocity_[2]); DEBUG_PRINT_FMT("mv: {}", mv); DEBUG_PRINT_FMT("epsilon: {}", epsilon); DEBUG_PRINT_FMT("sigma: {}", sigma); @@ -41,20 +41,21 @@ CuboidGenerator::CuboidGenerator(const dvec3 &corner, } void CuboidGenerator::generate(std::vector &particles) { - const int size = dimensions[0] * dimensions[1] * dimensions[2]; - particles.reserve(size); + const int size = dimensions_[0] * dimensions_[1] * dimensions_[2]; + const std::size_t offset = particles.size(); + particles.reserve(offset + size); DEBUG_PRINT("reserved: " + std::to_string(size) + "particles"); - for (int i = 0; i < dimensions[0]; i++) { - for (int j = 0; j < dimensions[1]; j++) { - for (int k = 0; k < dimensions[2]; k++) { - dvec3 position = {corner[0] + i * h, corner[1] + j * h, - corner[2] + k * h}; + for (int i = 0; i < dimensions_[0]; i++) { + for (int j = 0; j < dimensions_[1]; j++) { + for (int k = 0; k < dimensions_[2]; k++) { + dvec3 position = {corner_[0] + i * h_, corner_[1] + j * h_, + corner_[2] + k * h_}; - std::array V = - initialVelocity + - maxwellBoltzmannDistributedVelocity(mv, twoD ? 2 : 3); - particles.emplace_back(position, V, m, epsilon, sigma, type); + std::array v = + initial_velocity_ + + maxwellBoltzmannDistributedVelocity(mv_, two_d_ ? 2 : 3); + particles.emplace_back(position, v, m_, epsilon_, sigma_, type_); } } } diff --git a/src/defs/Generators/CuboidGenerator.h b/src/defs/Generators/CuboidGenerator.h index 7ca7b348..de92aea7 100644 --- a/src/defs/Generators/CuboidGenerator.h +++ b/src/defs/Generators/CuboidGenerator.h @@ -12,24 +12,18 @@ */ class CuboidGenerator final : public ParticleGenerator { private: - dvec3 corner; - ivec3 dimensions; - double h; - double m; - const dvec3 initialVelocity; - double mv; - /** - * technically these are only relevant for week 4 but here they dont really - * matter right now - */ - double epsilon; - double sigma; - const int type{}; - /** - * here this just describes the behaviour of the brownian motion - * initialization - */ - const bool twoD{}; + dvec3 corner_; + ivec3 dimensions_; + double h_; + double m_; + const dvec3 initial_velocity_; + double mv_; + double epsilon_; + double sigma_; + const int type_; + // two_d_ is the dimensions of the entire simulation and here it is used in + // brownian motion init + const bool two_d_; public: /** @@ -38,18 +32,18 @@ class CuboidGenerator final : public ParticleGenerator { * @param dimensions number of particles in each unit direction * @param h distance between pairwise particles * @param m mass of the particles in the cuboid - * @param initialVelocity velocity (imagine this as a net-zero movement of all - * particles) given by the predefined maxwell-boltzmann generator + * @param initial_velocity velocity (imagine this as a net-zero movement of + * all particles) given by the predefined maxwell-boltzmann generator * @param epsilon lj - epsilon * @param sigma lj - sigma * @param mv temperature of our system * @param type type of the particle in the system - * @param twoD dimension of velocity vector of brownian motion + * @param two_d dimension of velocity vector of brownian motion */ CuboidGenerator(const dvec3 &corner, const std::array &dimensions, double h, double m, - const std::array &initialVelocity, double mv, - double epsilon, double sigma, int type, bool twoD); + const std::array &initial_velocity, double mv, + double epsilon, double sigma, int type, bool two_d); /** * @brief generates particles in the shape of a cuboid diff --git a/src/defs/Generators/MembraneGenerator.cpp b/src/defs/Generators/MembraneGenerator.cpp new file mode 100644 index 00000000..9ead749b --- /dev/null +++ b/src/defs/Generators/MembraneGenerator.cpp @@ -0,0 +1,118 @@ +// +// Created by jkr on 10/31/24. +// +#include "MembraneGenerator.h" + +#include +#include + +#include "debug/debug_print.h" +#include "utils/ArrayUtils.h" +#include "utils/MaxwellBoltzmannDistribution.h" +#include "utils/SpdWrapper.h" + +MembraneGenerator::MembraneGenerator( + const dvec3 &corner, const std::array &dimensions, const double h, + const double m, const std::array &initial_velocity, + const double mv, const double epsilon, const double sigma, const int type, + const bool two_d, const std::vector &indices) + : corner_(corner), + dimensions_(dimensions), + h_(h), + m_(m), + initial_velocity_(initial_velocity), + mv_(mv), + epsilon_(epsilon), + sigma_(sigma), + type_(type), + two_d_(two_d), + indices_(indices) { + DEBUG_PRINT_FMT("CuboidGenerator of dim {} created with parameters:", + two_d ? 2 : 3); + DEBUG_PRINT_FMT("corner: ({}, {}, {})", corner[0], corner[1], corner[2]); + DEBUG_PRINT_FMT("dimensions: ({}, {}, {})", dimensions[0], dimensions[1], + dimensions[2]); + DEBUG_PRINT_FMT("h: {}", h); + DEBUG_PRINT_FMT("m: {}", m); + DEBUG_PRINT_FMT("initialVelocity: ({}, {}, {})", initial_velocity[0], + initial_velocity[1], initial_velocity[2]); + DEBUG_PRINT_FMT("mv: {}", mv); + DEBUG_PRINT_FMT("epsilon: {}", epsilon); + DEBUG_PRINT_FMT("sigma: {}", sigma); + DEBUG_PRINT_FMT("type: {}", type); +} + +void MembraneGenerator::generate(std::vector &particles) { + const int size = dimensions_[0] * dimensions_[1] * dimensions_[2]; + const std::size_t offset = particles.size(); + particles.reserve(offset + size); + DEBUG_PRINT("reserved: " + std::to_string(size) + "particles"); + + for (int i = 0; i < dimensions_[0]; i++) { + for (int j = 0; j < dimensions_[1]; j++) { + for (int k = 0; k < dimensions_[2]; k++) { + dvec3 position = {corner_[0] + i * h_, corner_[1] + j * h_, + corner_[2] + k * h_}; + + std::array v = + initial_velocity_ + + maxwellBoltzmannDistributedVelocity(mv_, two_d_ ? 2 : 3); + + Particle particle(position, v, m_, epsilon_, sigma_, type_); + + particles.push_back(std::move(particle)); + for (ivec3 vec : indices_) { + if (ivec3{i, j, k} == vec) { + SpdWrapper::get()->info("{}, {}, {} matched index {}", i, j, k, + particles.back().getId()); + ids_.push_back(particles.back().getId()); + } + } + } + } + } + + for (int i = 0; i < dimensions_[0]; i++) { + for (int j = 0; j < dimensions_[1]; j++) { + for (int k = 0; k < dimensions_[2]; k++) { + const std::size_t current_index = + i * dimensions_[1] * dimensions_[2] + j * dimensions_[2] + k; + + // Iterate over all neighbors including diagonals + for (int di = -1; di <= 1; di++) { + for (int dj = -1; dj <= 1; dj++) { + for (int dk = -1; dk <= 1; dk++) { + if (di == 0 && dj == 0 && dk == 0) { + continue; + } + + const long ni = i + di; + const long nj = j + dj; + const long nk = k + dk; + + if (ni >= 0 && ni < dimensions_[0] && nj >= 0 && + nj < dimensions_[1] && nk >= 0 && nk < dimensions_[2]) { + const long neighbor_index = + ni * dimensions_[1] * dimensions_[2] + nj * dimensions_[2] + + nk; + const bool is_diagonal = (di != 0) + (dj != 0) + (dk != 0) > 1; + Particle *neighbor_particle = &particles[neighbor_index]; + if (neighbor_particle->getId() == 0) { + std::cout + << std::hex << std::setw(16) << std::setfill('0') + << reinterpret_cast(&particles[neighbor_index]) + << std::dec << std::endl; + } + particles[current_index].pushBackNeighbour( + is_diagonal, reinterpret_cast(neighbor_particle)); + } + } + } + } + } + } + } + DEBUG_PRINT("particles: " + std::to_string(particles.size())); +} + +std::vector MembraneGenerator::getIndices() const { return ids_; } diff --git a/src/defs/Generators/MembraneGenerator.h b/src/defs/Generators/MembraneGenerator.h new file mode 100644 index 00000000..983fea56 --- /dev/null +++ b/src/defs/Generators/MembraneGenerator.h @@ -0,0 +1,79 @@ +// +// Created by jkr on 10/31/24. +// + +#ifndef MEMBRANEGENERATOR_H +#define MEMBRANEGENERATOR_H +#pragma once +#include "ParticleGenerator.h" + +/** + * @brief Generates Particles to a container in the shape of a cuboid [dim1, + * dim2, dim3] + */ +class MembraneGenerator final : public ParticleGenerator { + private: + dvec3 corner_; + ivec3 dimensions_; + double h_; + double m_; + const dvec3 initial_velocity_; + double mv_; + double epsilon_; + double sigma_; + const int type_; + /** + * here this just describes the behaviour of the brownian motion + * initialization + */ + const bool two_d_; + std::vector ids_{}; + std::vector indices_{}; + + public: + /** + * @brief Constructor for the particle generator + * @param corner Lower left corner / origin of the shape + * @param dimensions number of particles in each unit direction + * @param h distance between pairwise particles + * @param m mass of the particles in the cuboid + * @param initial_velocity velocity (imagine this as a net-zero movement of + * all particles) given by the predefined maxwell-boltzmann generator + * @param epsilon lj - epsilon + * @param sigma lj - sigma + * @param mv temperature of our system + * @param type type of the particle in the system + * @param two_d dimension of velocity vector of brownian motion + * @param indices Grid coordinates, so the dimension of the cuboids (not + * spatial) + */ + MembraneGenerator(const dvec3 &corner, const std::array &dimensions, + double h, double m, + const std::array &initial_velocity, double mv, + double epsilon, double sigma, int type, bool two_d, + const std::vector &indices); + + /** + * @brief generates particles in the shape of a cuboid, and connects each + * particle to its direct neighbours. Furthermore, writes if each neighbour + * is diagonal or not. Neighbours are directly saved by uint_ptr + * @param particles vector from container which contains the vector in which + * this cuboid is saved in + */ + void generate(std::vector &particles) override; + + /** + * @return particle ids (so matching by p.getId()) + */ + [[nodiscard]] std::vector getIndices() const; + + /** + * @brief calculates the particle ids from the cuboid coordinates + * @param indices the cuboid coordinates of the membrane + */ + void setTargetIndices(const std::vector &indices) { + this->indices_ = indices; + } +}; + +#endif // MEMBRANEGENERATOR_H diff --git a/src/defs/Generators/ParticleGenerator.h b/src/defs/Generators/ParticleGenerator.h index b59b7e84..d60e479c 100644 --- a/src/defs/Generators/ParticleGenerator.h +++ b/src/defs/Generators/ParticleGenerator.h @@ -12,6 +12,9 @@ */ class ParticleGenerator { public: + /** + * @brief Virtual destructor of Particle generator + */ virtual ~ParticleGenerator() = default; /** diff --git a/src/defs/Generators/SpheroidGenerator.cpp b/src/defs/Generators/SpheroidGenerator.cpp index a8b3d789..21ab327f 100644 --- a/src/defs/Generators/SpheroidGenerator.cpp +++ b/src/defs/Generators/SpheroidGenerator.cpp @@ -11,29 +11,29 @@ SpheroidGenerator::SpheroidGenerator(const dvec3& origin, const int radius, const double h, const double m, - const dvec3& initialVelocity, + const dvec3& initial_velocity, const double epsilon, const double sigma, const int type, const double mv, - const bool twoD) - : origin(origin), - radius(radius - - 1), // needs to be minus one because we consider the origin as one - h(h), - m(m), - initialVelocity(initialVelocity), - epsilon(epsilon), - sigma(sigma), - type(type), - mv(mv), - twoD(twoD) { + const bool two_d) + : origin_(origin), + radius_(radius - 1), // needs to be minus one because we consider the + // origin as one + h_(h), + m_(m), + initial_velocity_(initial_velocity), + epsilon_(epsilon), + sigma_(sigma), + type_(type), + mv_(mv), + two_d_(two_d) { DEBUG_PRINT_FMT("SpheroidGenerator of dim {} created with parameters:", - twoD ? 2 : 3); + two_d ? 2 : 3); DEBUG_PRINT_FMT("origin: ({}, {}, {})", origin[0], origin[1], origin[2]); DEBUG_PRINT_FMT("radius: {}", radius + 1); DEBUG_PRINT_FMT("h: {}", h); DEBUG_PRINT_FMT("m: {}", m); - DEBUG_PRINT_FMT("initialVelocity: ({}, {}, {})", initialVelocity[0], - initialVelocity[1], initialVelocity[2]); + DEBUG_PRINT_FMT("initialVelocity: ({}, {}, {})", initial_velocity[0], + initial_velocity[1], initial_velocity[2]); DEBUG_PRINT_FMT("mv: {}", mv); DEBUG_PRINT_FMT("epsilon: {}", epsilon); DEBUG_PRINT_FMT("sigma: {}", sigma); @@ -45,27 +45,28 @@ void SpheroidGenerator::generate(std::vector& particles) { // Approximate the size by area of disk and volume of sphere // There are between (r-h)/h, (r+h)/h particles on one bisector, so we // overestimate with (r+h) - if (twoD) { - size = static_cast(M_PI * std::pow((radius + h) / h, 2)); + if (two_d_) { + size = static_cast(M_PI * std::pow((radius_ + h_) / h_, 2)); } else { - size = static_cast((4.0 / 3.0) * M_PI * std::pow((radius + h) / h, 3)); + size = + static_cast((4.0 / 3.0) * M_PI * std::pow((radius_ + h_) / h_, 3)); } particles.reserve(size); DEBUG_PRINT("Reserved " + std::to_string(size)); - for (int i = -radius; i <= radius; i++) { - for (int j = -radius; j <= radius; j++) { - for (int k = -radius; k <= radius; k++) { - if (twoD && k != 0) { + for (int i = -radius_; i <= radius_; i++) { + for (int j = -radius_; j <= radius_; j++) { + for (int k = -radius_; k <= radius_; k++) { + if (two_d_ && k != 0) { continue; } - const double spaceRadius = radius * h; - dvec3 point = {i * h, j * h, k * h}; - if (const double dist = ArrayUtils::L2Norm(1 / spaceRadius * point); + const double space_radius = radius_ * h_; + dvec3 point = {i * h_, j * h_, k * h_}; + if (const double dist = ArrayUtils::L2Norm(1 / space_radius * point); dist <= 1.0) { - dvec3 position = origin + point; - dvec3 V = initialVelocity + - maxwellBoltzmannDistributedVelocity(mv, twoD ? 2 : 3); - particles.emplace_back(position, V, m, epsilon, sigma, type); + dvec3 position = origin_ + point; + dvec3 v = initial_velocity_ + + maxwellBoltzmannDistributedVelocity(mv_, two_d_ ? 2 : 3); + particles.emplace_back(position, v, m_, epsilon_, sigma_, type_); } } } diff --git a/src/defs/Generators/SpheroidGenerator.h b/src/defs/Generators/SpheroidGenerator.h index be54e025..a8613067 100644 --- a/src/defs/Generators/SpheroidGenerator.h +++ b/src/defs/Generators/SpheroidGenerator.h @@ -11,28 +11,62 @@ */ class SpheroidGenerator final : public ParticleGenerator { private: - dvec3 origin{}; + dvec3 origin_; /** * @brief this is an integer as it's the number of particles possible along * the radius of the spheroid, so we move along the radii in increments of h */ - const int radius; - double h{}; - double m{}; - const dvec3 initialVelocity; - double epsilon{}; - double sigma{}; - const int type{}; - double mv{}; + const int radius_; + /** + * distance between particles + */ + double h_; + /** + * mass of particles + */ + double m_; + /** + * initial velocity of particles + */ + const dvec3 initial_velocity_; + /** + * epsilon of particles + */ + double epsilon_; + /** + * sigma of particles + */ + double sigma_; + /** + * type of particles + */ + const int type_; + /** + * Mean velocity for brownian motion + */ + double mv_; /** * only if this is passed with true, spheres will be two Dimensional */ - const bool twoD{}; + const bool two_d_; public: + /** + * @brief Instantiate SpheroidGenerator + * @param origin Origin of the generator + * @param radius Radius + * @param h Distance between particles + * @param m mass of particles + * @param initial_velocity initial velocity of generated particles + * @param epsilon epsilon of generated particles + * @param sigma sigma of generated particles + * @param type type of generated particles + * @param mv brownian mean velocity + * @param two_d whether generator is 2D or 3D + */ SpheroidGenerator(const dvec3 &origin, int radius, double h, double m, - const dvec3 &initialVelocity, double epsilon, double sigma, - int type, double mv, bool twoD); + const dvec3 &initial_velocity, double epsilon, double sigma, + int type, double mv, bool two_d); /** * @brief generates a 1 or 2 sphere of particles, vector size is approximated diff --git a/src/defs/Particle.cpp b/src/defs/Particle.cpp index 46de5110..488cf1d2 100644 --- a/src/defs/Particle.cpp +++ b/src/defs/Particle.cpp @@ -10,37 +10,39 @@ #include "utils/ArrayUtils.h" #include "utils/SpdWrapper.h" -Particle::Particle(int type_arg) { - type = type_arg; - // DEBUG_PRINT("Particle generated!"); - f = {0., 0., 0.}; - old_f = {0., 0., 0.}; +int Particle::global_id_counter_ = 0; + +Particle::Particle(const int type) { + type_ = type; + f_ = {0., 0., 0.}; + old_f_ = {0., 0., 0.}; } Particle::Particle(const Particle &other) { - x = other.x; - v = other.v; - f = other.f; - old_f = other.old_f; - m = other.m; - type = other.type; - epsilon = other.epsilon; - sigma = other.sigma; - // DEBUG_PRINT("Particle generated by copy!"); + x_ = other.x_; + v_ = other.v_; + f_ = other.f_; + old_f_ = other.old_f_; + m_ = other.m_; + type_ = other.type_; + epsilon_ = other.epsilon_; + sigma_ = other.sigma_; + id_ = other.id_; + neighbours_ = other.neighbours_; } Particle::Particle(const std::array &x_arg, const std::array &v_arg, const double m_arg, - const double _epsilon, const double _sigma, int _type) { - x = x_arg; - v = v_arg; - m = m_arg; - type = _type; - f = {0., 0., 0.}; - old_f = {0., 0., 0.}; - sigma = _sigma; - epsilon = _epsilon; - // DEBUG_PRINT("Particle generated!"); + const double epsilon, const double sigma, const int type) { + x_ = x_arg; + v_ = v_arg; + m_ = m_arg; + type_ = type; + f_ = {0., 0., 0.}; + old_f_ = {0., 0., 0.}; + sigma_ = sigma; + epsilon_ = epsilon; + id_ = global_id_counter_++; } Particle::Particle(const std::array &x_arg, @@ -49,70 +51,44 @@ Particle::Particle(const std::array &x_arg, const std::array &old_f_arg, const double m_arg, const int type_arg, const double epsilon_arg, const double sigma_arg) - : x(x_arg), - v(v_arg), - f(f_arg), - old_f(old_f_arg), - m(m_arg), - type(type_arg), - epsilon(epsilon_arg), - sigma(sigma_arg) {} - -Particle::~Particle() { /*DEBUG_PRINT("Particle destructed!");*/ } - -const std::array &Particle::getX() const { return x; } - -const std::array &Particle::getV() const { return v; } - -const std::array &Particle::getF() const { return f; } - -const std::array &Particle::getOldF() const { return old_f; } - -double Particle::getM() const { return m; } - -int Particle::getType() const { return type; } - -double Particle::getEpsilon() const { return epsilon; } - -double Particle::getSigma() const { return sigma; } - -void Particle::setF(const std::array &F) { f = F; } - -void Particle::setV(const std::array &V) { v = V; } - -void Particle::setX(const std::array &X) { x = X; } - -void Particle::setOldF(const dvec3 &oF) { old_f = oF; } - -void Particle::setEpsilon(const double &Epsilon) { epsilon = Epsilon; } - -void Particle::setSigma(const double &Sigma) { sigma = Sigma; } - -void Particle::addV(const dvec3 &V) { v = v + V; } - -void Particle::subV(const dvec3 &V) { v = v - V; } - -void Particle::mulV(const double &scalar) { v = scalar * v; } - -void Particle::addF(const dvec3 &F) { f = f + F; } + : x_(x_arg), + v_(v_arg), + f_(f_arg), + old_f_(old_f_arg), + m_(m_arg), + type_(type_arg), + epsilon_(epsilon_arg), + sigma_(sigma_arg), + id_(global_id_counter_++) {} + +Particle::~Particle() = default; + +const std::vector> &Particle::getNeighbours() const { + return neighbours_; +} -void Particle::subF(const dvec3 &F) { f = f - F; } +void Particle::pushBackNeighbour(bool diag, long particle) { + neighbours_.emplace_back(diag, particle); +} void Particle::updateForceInTime() { - old_f = f; - f = {0., 0., 0.}; + old_f_ = f_; + f_ = {0., 0., 0.}; } +int Particle::getId() const { return id_; } + std::string Particle::toString() const { std::stringstream stream; - stream << "Particle: X:" << x << " v: " << v << " f: " << f - << " old_f: " << old_f << " type: " << type; + stream << "Particle: X:" << x_ << " v: " << v_ << " f: " << f_ + << " old_f: " << old_f_ << " type: " << type_; return stream.str(); } bool Particle::operator==(const Particle &other) const { - return (x == other.x) and (v == other.v) and (f == other.f) and - (type == other.type) and (m == other.m) and (old_f == other.old_f); + return (x_ == other.x_) and (v_ == other.v_) and (f_ == other.f_) and + (type_ == other.type_) and (m_ == other.m_) and + (old_f_ == other.old_f_); } std::ostream &operator<<(std::ostream &stream, const Particle &p) { diff --git a/src/defs/Particle.h b/src/defs/Particle.h index 018a69ef..84f13823 100644 --- a/src/defs/Particle.h +++ b/src/defs/Particle.h @@ -8,65 +8,77 @@ #pragma once #include +#include #include #include "defs/types.h" +#include "utils/ArrayUtils.h" class Particle final { private: + static int global_id_counter_; /** * Position of the particle */ - std::array x{}; + std::array x_{}; /** * Velocity of the particle */ - std::array v{}; + std::array v_{}; /** * Force effective on this particle */ - std::array f{}; + std::array f_{}; /** * Force which was effective on this particle */ - std::array old_f{}; + std::array old_f_{}; /** * Mass of this particle */ - double m{}; + double m_{}; /** * Type of the particle. Use it for whatever you want (e.g. to separate * molecules belonging to different bodies, matters, and so on) + * negative types are immovable and are being ignored by some calculations */ - int type{}; + int type_{}; /** - * depth of the potential well + * depth of the potential-well * used in the caluclation of lennard-jones force */ - double epsilon{}; + double epsilon_{}; /** * distance from the particle at which the potential is zero \n * used in the calculation of lennard-jones force */ - double sigma{}; + double sigma_{}; + + /** + * unique identifier for every particle + */ + int id_{}; + + /** + * neighbouring cells for the membranes, saved as [diagonal, address] + */ + std::vector> neighbours_; public: explicit Particle(int type = 0); Particle(const Particle &other); - Particle( - // for visualization, we need always 3 coordinates - // -> in case of 2d, we use only the first and the second - const std::array &x_arg, const std::array &v_arg, - double m_arg, double _epsilon, double _sigma, int type = 0); + Particle(const std::array &x_arg, + const std::array &v_arg, double m_arg, double epsilon, + double sigma, int type = 0); explicit Particle(const std::array &x_arg, const std::array &v_arg, @@ -76,49 +88,142 @@ class Particle final { ~Particle(); - [[nodiscard]] const std::array &getX() const; + [[nodiscard]] const std::array &getX() const { return x_; } + + [[nodiscard]] const std::array &getV() const { return v_; } + + [[nodiscard]] const std::array &getF() const { return f_; } - [[nodiscard]] const std::array &getV() const; + [[nodiscard]] const std::array &getOldF() const { return old_f_; } - [[nodiscard]] const std::array &getF() const; + [[nodiscard]] double getM() const { return m_; } - [[nodiscard]] const std::array &getOldF() const; + [[nodiscard]] int getType() const { return type_; } - [[nodiscard]] double getM() const; + [[nodiscard]] double getEpsilon() const { return epsilon_; } - [[nodiscard]] int getType() const; + [[nodiscard]] double getSigma() const { return sigma_; } - [[nodiscard]] double getEpsilon() const; + [[nodiscard]] const std::vector> &getNeighbours() + const; - [[nodiscard]] double getSigma() const; + void setF(const std::array &f) { f_ = f; } - void setF(const std::array &F); + void setX(const std::array &x) { x_ = x; } - void setX(const std::array &X); + void setV(const std::array &v) { v_ = v; } - void setV(const std::array &V); + void setOldF(const dvec3 &old_f) { old_f_ = old_f; } - void setOldF(const dvec3 &oF); + void setEpsilon(const double &epsilon) { epsilon_ = epsilon; } - void setEpsilon(const double &epsilon); + void setSigma(const double &sigma) { sigma_ = sigma; } - void setSigma(const double &sigma); + /** + * @brief Push Neighbour reference to a particle + * @param diag Whether neighbour is diagonal + * @param particle Particle reference in form of an long + */ + void pushBackNeighbour(bool diag, long particle); + /** + * @brief OldF = F; F = 0 + */ void updateForceInTime(); - void subV(const dvec3 &V); + void subV(const dvec3 &v) { v_ = v_ - v; } - void addV(const dvec3 &V); + void addV(const dvec3 &v) { v_ = v_ + v; } - void mulV(const double &scalar); + void mulV(const double &scalar) { v_ = scalar * v_; } + + void addF(const dvec3 &f) { f_ = f_ + f; } + + void subF(const dvec3 &f) { f_ = f_ - f; } + + /** + * @brief Directly set neighbours of a particle + * @param new_neighbours Vector of neighbours (bool == diagonal, size_t == + * address) + */ + void setNeighbours( + const std::vector> &new_neighbours) { + neighbours_ = new_neighbours; + } - void addF(const dvec3 &F); + /** + * @brief Clear neighbour references + */ + void resetNeighbours() { neighbours_ = {}; } - void subF(const dvec3 &F); + /** + * @brief Retrieve particle id + * @return particle id + */ + [[nodiscard]] int getId() const; + /** + * @brief Comparison operator + * @param other Other particle to be compare against + * @return Whether particle is seen as equivalent + */ bool operator==(const Particle &other) const; + /** + * @brief Get debug string from particle + * @return String + */ [[nodiscard]] std::string toString() const; + + /** + * @brief Copy assignment operator for the Particle class. + * @param other The Particle instance to copy from + * @return a reference to the current particle + */ + Particle &operator=(const Particle &other) { + if (this != &other) { + x_ = other.x_; + v_ = other.v_; + f_ = other.f_; + old_f_ = other.old_f_; + m_ = other.m_; + type_ = other.type_; + id_ = other.id_; + epsilon_ = other.epsilon_; + sigma_ = other.sigma_; + neighbours_ = other.neighbours_; // Shallow copy of shared_ptr + } + return *this; + } + + /** + * @brief Move assignment operator for Particle. Transfers the ownership of + * the neighbours vector + * + * @param other The Particle instance to move from + */ + Particle &operator=(Particle &&other) noexcept { + if (this != &other) { + x_ = other.x_; + v_ = other.v_; + f_ = other.f_; + old_f_ = other.old_f_; + m_ = other.m_; + type_ = other.type_; + id_ = other.id_; + epsilon_ = other.epsilon_; + sigma_ = other.sigma_; + neighbours_ = + std::move(other.neighbours_); // Transfer ownership of shared_ptr + } + return *this; + } }; +/** + * @brief Write Particle to stream + * @param stream Output stream + * @param p particle to be written + * @return passed in stream + */ std::ostream &operator<<(std::ostream &stream, const Particle &p); diff --git a/src/defs/Simulation.h b/src/defs/Simulation.h index 23732f94..29c3f1fa 100644 --- a/src/defs/Simulation.h +++ b/src/defs/Simulation.h @@ -7,27 +7,91 @@ #pragma once #include - +#ifdef _OPENMP +#include +#endif #include "defs/types.h" -#include "forces/InteractiveForce.h" #include "forces/SingularForce.h" #include "utils/SpdWrapper.h" +/** + * @brief Parallelization Strategy + */ +enum ParallelStrategy { STRATEGY_1, STRATEGY_2, STRATEGY_3 }; + +/** + * @brief Index Force config struct + */ +struct IndexForceConfig { + /** + * Cuboid coordinates to be targetted + */ + std::vector indeces{}; + /** + * Indices to be targetted + */ + std::vector ids{}; + + /** + * End time of the force application + */ + double end_time{}; + /** + * force vector applied + */ + dvec3 force_values{}; +}; + /** * @brief holds the specification for the LinkedCellsContainer */ struct LinkedCellsConfig { - ivec3 domain; // size of the dimensions + /** + * Domain size + */ + ivec3 domain; + /** + * Cutoff radius of the Linked-Cells algorithm + */ double cutoff_radius; + /** + * Boundary Type enum for different boundary conditions + */ enum BoundaryType { Outflow, Reflective, Periodic } boundary_type; + /** + * Struct which holds the boundary config for the linked cells container + */ struct BoundaryConfig { + /** + * high x boundary type + */ BoundaryType x_high; + /** + * low x boundary type + */ BoundaryType x_low; + /** + * high y boundary type + */ BoundaryType y_high; + /** + * low y boundary type + */ BoundaryType y_low; + /** + * high z boundary type + */ BoundaryType z_high; + /** + * low z boundary type + */ BoundaryType z_low; } boundary_config; + + /** + * Whether the simulation uses membranes or not + */ + bool is_membrane = false; }; /** @@ -37,44 +101,170 @@ struct LinkedCellsConfig { */ struct DirectSumConfig {}; +/** + * @brief holds the specification for the SingularGravity force + */ struct SingularGravityConfig { + /** + * "gravitational" force applied + */ double g{}; + /** + * axis in which the force is applied + */ + int axis{}; }; -struct HarmonicForceConfig {}; +/** + * @brief holds the harmonic force parameters + */ +struct HarmonicForceConfig { + /** + * average bond length + */ + double r_0{}; + /** + * Spring/stiffness constant + */ + double k{}; +}; +/** + * @brief holds the LennardJones force parameters + */ struct LennardJonesConfig {}; +/** + * @brief hold the Truncated LennardJones force parameters + */ +struct TruncatedLennardJonesConfig {}; + +/** + * @brief holds the interactive gravity parameters + */ struct GravityConfig {}; /** * @brief holds instance data for Thermostat */ struct ThermostatConfig { - double T_init{}; - double T_target{}; - double deltaT{}; + /** + * init temperature + */ + double t_init{}; + /** + * target temperature + */ + double t_target{}; + /** + * delta time + */ + double delta_t{}; + /** + * periodicity of application + */ int n_thermostat{}; + /** + * Whether thermostat is relative + */ bool use_relative{}; + /** + * Whether thermostat uses thermal motion + */ + bool use_thermal_motion{}; + /** + * Whether thermostat is 2D or 3D + */ + bool two_d{}; +}; + +/** + * @brief Statistics configuration struct + */ +struct StatisticsConfig { + /** + * Inicates whether to write statistics to a file + */ + bool calc_stats{}; + /** + * Number of x bins + */ + int x_bins{}; + /** + * Number of y bins + */ + int y_bins{}; + /** + * Periodicity of outputting to files + */ + int output_interval{}; + /** + * Path to the csv containing the velocity information + */ + std::string velocity_output_location{}; + /** + * Path to the csv containing the density information + */ + std::string density_output_location{}; }; /** - * @brief struct to hold command line arguments + * @brief Supertype for all Singular Forces */ +using SingularForceTypes = + std::variant; +/** + * @brief Supertype for all Interactive Forces + */ +using InteractiveForceTypes = std::variant; + +/** + * @brief Struct which hold the simulation arguments + */ struct Arguments { - using SingularForceTypes = - std::variant; - using InteractiveForceTypes = std::variant; + /** + * simulation end time + */ double t_end; + /** + * simulation delta time + */ double delta_t; - enum ForceType { LennardJones, Gravity } force_type; - enum SingularForceType { SingularGravity } singular_force_type; + /** + * Thermostat config + */ ThermostatConfig thermostat_config; + /** + * Whether to use the thermostat + */ bool use_thermostat; + /** + * Which container to use + */ std::variant container_data; + /** + * Vector of applied singular forces + */ std::vector singular_force_types; + /** + * Vector of applied interactive forces + */ std::vector interactive_force_types; + /** + * Vector of applied index forces + */ + std::vector index_force_configs; + /** + * Statistics configuration + */ + StatisticsConfig statistics_config; + + /** + * Parallelization strategy + */ + ParallelStrategy strategy; }; /** @@ -117,19 +307,19 @@ inline void printConfiguration(const Arguments& args) { logger->info("delta_t: {}", args.delta_t); logger->info("Singular Forces:"); if (args.use_thermostat) { - logger->info("Thermostat: T_init {}", args.thermostat_config.T_init); - logger->info("--- T_target: {}", args.thermostat_config.T_target); - logger->info("--- deltaT: {}", args.thermostat_config.deltaT); + logger->info("Thermostat: T_init {}", args.thermostat_config.t_init); + logger->info("--- T_target: {}", args.thermostat_config.t_target); + logger->info("--- deltaT: {}", args.thermostat_config.delta_t); } if (std::holds_alternative(args.container_data)) { logger->info("Container Type: Linked Cells"); - const auto& [domain, cutoff_radius, boundary_type, boundary_config] = + const auto& [domain, cutoff_radius, boundary_type, boundary_config, + is_membrane] = std::get(args.container_data); logger->info("-- Domain: ({}, {}, {})", domain[0], domain[1], domain[2]); logger->info("-- Cutoff Radius: {}", cutoff_radius); - logger->info("Boundary Configuration:"); logger->info("------------------------"); @@ -144,7 +334,9 @@ inline void printConfiguration(const Arguments& args) { } else { logger->info("Container Type: Direct Sum"); } - +#ifdef _OPENMP + logger->info("Number of Threads: {}", omp_get_max_threads()); +#endif logger->info("============================"); } diff --git a/src/defs/Thermostat.cpp b/src/defs/Thermostat.cpp index 93030bd7..16f578f9 100644 --- a/src/defs/Thermostat.cpp +++ b/src/defs/Thermostat.cpp @@ -5,21 +5,24 @@ #include "Thermostat.h" #include "containers/ParticleContainer.h" +#include "debug/debug_print.h" #include "utils/ArrayUtils.h" Thermostat::Thermostat(const ThermostatConfig &config) { - this->T_init = config.T_init; - this->T_target = config.T_target; - this->d_temp = config.deltaT; - this->n_thermostat = config.n_thermostat; - this->use_relative = config.use_relative; + this->t_init_ = config.t_init; + this->t_target_ = config.t_target; + this->delta_temp_ = config.delta_t; + this->n_thermostat_ = config.n_thermostat; + this->use_relative_ = config.use_relative; + this->use_thermal_motion_ = config.use_thermal_motion; + this->dimension_ = config.two_d ? 2 : 3; } -double Thermostat::getTemperature(ParticleContainer &particle_container) { - constexpr double D = 2; // TODO: make global so its 3D - const auto E_kin = particle_container.getKineticEnergy(); - return (2 * E_kin) / - (D * static_cast(particle_container.getParticles().size())); +double Thermostat::getTemperature(ParticleContainer &particle_container) const { + const auto e_kin = particle_container.getKineticEnergy(); + return (2 * e_kin) / + (dimension_ * + static_cast(particle_container.getParticles().size())); } dvec3 Thermostat::getGlobalVelocity(ParticleContainer &particle_container) { @@ -32,37 +35,85 @@ dvec3 Thermostat::getGlobalVelocity(ParticleContainer &particle_container) { } void Thermostat::applyBeta(ParticleContainer &particle_container, - const double beta) const { + const double beta) { particle_container.singleIterator([&beta](Particle &p) { p.mulV(beta); }); } +void Thermostat::applyThermalBeta(ParticleContainer &particle_container, + const double beta, + const dvec3 &avg_velocity) { + particle_container.singleIterator([&beta, avg_velocity](Particle &p) { + p.setV(avg_velocity + beta * (p.getV() - avg_velocity)); + }); +} + +dvec3 Thermostat::getAverageVelocity(ParticleContainer &particle_container) { + dvec3 total_velocity = {0.0, 0.0, 0.0}; + const auto c = + static_cast(particle_container.getParticleCount() - + particle_container.getSpecialParticleCount()); + + for (const auto &p : particle_container.getParticles()) { + if (p->getType() >= 0) { + total_velocity = total_velocity + p->getV(); + } + } + + return {total_velocity[0] / c, total_velocity[1] / c, total_velocity[2] / c}; +} + +double Thermostat::getThermalTemperature(ParticleContainer &particle_container, + dvec3 avg_velocity) const { + double e_kin = 0.0; + particle_container.singleIterator([&e_kin, avg_velocity](const Particle &p) { + if (p.getType() < 0) return; // exclude walls + e_kin += p.getM() * ArrayUtils::L2InnerProduct(p.getV() - avg_velocity); + }); + + return e_kin / + (dimension_ * + static_cast(particle_container.getParticleCount() - + particle_container.getSpecialParticleCount())); +} + void Thermostat::setTemperature(ParticleContainer &particle_container) const { - const double current_temp = getTemperature(particle_container); -#ifndef BENCHMARK - SpdWrapper::get()->info("current temperature is {}", current_temp); -#endif - const double dT = T_target - current_temp; + dvec3 average_velocity; + double current_temp = 0; + + if (use_thermal_motion_) { + average_velocity = getAverageVelocity(particle_container); + current_temp = getThermalTemperature(particle_container, average_velocity); + } else { + current_temp = getTemperature(particle_container); + } + DEBUG_PRINT_FMT("current temperature is {}", current_temp); + const double d_t = t_target_ - current_temp; double adjustment = 0; - if (std::abs(dT) > d_temp) { - adjustment = (dT / std::abs(dT)) * d_temp; + if (std::abs(d_t) > delta_temp_) { + adjustment = (d_t / std::abs(d_t)) * delta_temp_; } else { - adjustment = dT; + adjustment = d_t; } -#ifndef BENCHMARK - SpdWrapper::get()->info("adjustment is {}", adjustment); -#endif + DEBUG_PRINT_FMT("adjustment is {}", adjustment); const double new_temp = current_temp + adjustment; -#ifndef BENCHMARK - SpdWrapper::get()->info("new_temp is {}", new_temp); -#endif + DEBUG_PRINT_FMT("new_temp is {}", new_temp); const double beta = std::sqrt(new_temp / current_temp); + DEBUG_PRINT_FMT("beta is {}", beta); + if (use_thermal_motion_) { + applyThermalBeta(particle_container, beta, average_velocity); + } else { + applyBeta(particle_container, beta); + } #ifndef BENCHMARK - SpdWrapper::get()->info("beta is {}", beta); +#ifdef DEBUG + average_velocity = getAverageVelocity(particle_container); + auto temp_after = getThermalTemperature(particle_container, average_velocity); + DEBUG_PRINT_FMT("temp_after is {}", temp_after); #endif - applyBeta(particle_container, beta); -#ifndef BENCHMARK - auto temp_after = getTemperature(particle_container); - SpdWrapper::get()->info("temp_after is {}", temp_after); #endif -} \ No newline at end of file +} + +int Thermostat::getNThermostat() const { return n_thermostat_; } + +double Thermostat::getTTarget() const { return t_target_; } diff --git a/src/defs/Thermostat.h b/src/defs/Thermostat.h index 3ebe5332..c1af4f12 100644 --- a/src/defs/Thermostat.h +++ b/src/defs/Thermostat.h @@ -5,19 +5,28 @@ #ifndef THERMOSTAT_H #define THERMOSTAT_H #pragma once + #include "Simulation.h" #include "containers/ParticleContainer.h" + /** - * adjusts the temperature of the system at a given periodic frequency + * @brief adjusts the temperature of the system at a given periodic frequency */ class Thermostat { - public: - double T_init{}; - double T_target{}; - double d_temp{}; - int n_thermostat{}; - bool use_relative{}; + private: + double t_init_; + double t_target_; + double delta_temp_; + int n_thermostat_; + bool use_relative_; + bool use_thermal_motion_; + int dimension_; + public: + /** + * @brief Instantiate a thermostat + * @param config Thermostat config to be used + */ explicit Thermostat(const ThermostatConfig& config); /** @@ -25,7 +34,7 @@ class Thermostat { * @param particle_container container with the particle system * @return the temperature calculated as (2 * E_kin) / 2 * (#dim * #particles) */ - static double getTemperature(ParticleContainer& particle_container); + double getTemperature(ParticleContainer& particle_container) const; /** * @calculates the average global velocity @@ -34,17 +43,55 @@ class Thermostat { */ static dvec3 getGlobalVelocity(ParticleContainer& particle_container); + /** + * @calculates the average global velocity excluding special particles + * @param particle_container container + * @return the average global velocity + */ + static dvec3 getAverageVelocity(ParticleContainer& particle_container); + /** * @brief scales the temperature relatively or absolutely using beta * @param particle_container container * @param beta the scaling coefficient sqrt(T_new / T_current) */ - void applyBeta(ParticleContainer& particle_container, double beta) const; + static void applyBeta(ParticleContainer& particle_container, double beta); + + /** + * @brief scales the temperature relatively or absolutely using beta and + * ignores average velocity + * @param particle_container container + * @param beta the scaling coefficient sqrt(T_new / T_current) + * @param avg_velocity average velocity + */ + static void applyThermalBeta(ParticleContainer& particle_container, + double beta, const dvec3& avg_velocity); /** * @a wrapper to proved applyBeta with the necessary data * @param particle_container container */ void setTemperature(ParticleContainer& particle_container) const; + + /** + * @brief calculates the temperature according to the thermal motion + * @param particle_container container + * @param avg_velocity average velocity of particles + * @return the temperature according to the thermal motion + */ + double getThermalTemperature(ParticleContainer& particle_container, + dvec3 avg_velocity) const; + + /** + * @brief Get periodicity of thermostat application + * @return periodicity of thermostat application + */ + [[nodiscard]] int getNThermostat() const; + + /** + * @brief Get target temperature + * @return Target temperature + */ + [[nodiscard]] double getTTarget() const; }; #endif // THERMOSTAT_H diff --git a/src/defs/containers/DirectSumContainer.cpp b/src/defs/containers/DirectSumContainer.cpp index df2daf00..6e6b37b0 100644 --- a/src/defs/containers/DirectSumContainer.cpp +++ b/src/defs/containers/DirectSumContainer.cpp @@ -16,39 +16,37 @@ DirectSumContainer::DirectSumContainer() : ParticleContainer() { DEBUG_PRINT("DirectSumContainer::DirectSumContainer()"); - this->particles = {}; + this->particles_ = {}; } DirectSumContainer::DirectSumContainer(const std::vector& particles) : ParticleContainer() { DEBUG_PRINT("explicit DirectSumContainer::DirectSumContainer()"); for (const auto& particle : particles) { - this->particles.push_back(particle); + this->particles_.push_back(particle); } } -// DirectSumContainer::~DirectSumContainer(); - void DirectSumContainer::addParticle(const Particle& p) { - particles.push_back(p); + particles_.push_back(p); } void DirectSumContainer::addParticles(const std::vector& particles) { - for (const auto& p : particles) { + for (const Particle& p : particles) { addParticle(p); } } void DirectSumContainer::removeParticle(const Particle& p) { - particles.erase(std::remove_if(particles.begin(), particles.end(), - [&p](const Particle& q) { return p == q; }), - particles.end()); + particles_.erase(std::remove_if(particles_.begin(), particles_.end(), + [&p](const Particle& q) { return p == q; }), + particles_.end()); } std::vector DirectSumContainer::getParticles() { std::vector refs; - refs.reserve(particles.size()); - for (auto& p : particles) { + refs.reserve(particles_.size()); + for (auto& p : particles_) { refs.push_back(&p); } @@ -57,20 +55,20 @@ std::vector DirectSumContainer::getParticles() { std::vector DirectSumContainer::getParticlesObjects() { std::vector refs; - refs.reserve(particles.size()); - for (auto& p : particles) { + refs.reserve(particles_.size()); + for (auto& p : particles_) { refs.push_back(p); } return refs; } [[nodiscard]] std::size_t DirectSumContainer::size() const { - return particles.size(); + return particles_.size(); } void DirectSumContainer::singleIterator( const std::function& f) { - for (auto& p : particles) { + for (auto& p : particles_) { f(p); } } @@ -78,21 +76,45 @@ void DirectSumContainer::singleIterator( void DirectSumContainer::pairIterator( const std::function& f) { // note that the upper tri-diag matrix is iterated over - for (size_t i = 0; i < particles.size(); ++i) { - for (size_t j = i + 1; j < particles.size(); ++j) { - f(particles[i], particles[j]); + for (size_t i = 0; i < particles_.size(); ++i) { + for (size_t j = i + 1; j < particles_.size(); ++j) { + f(particles_[i], particles_[j]); } } } double DirectSumContainer::getKineticEnergy() { - double E_kin = 0.0; - singleIterator([&E_kin](const Particle& p) { - E_kin += 0.5 * p.getM() * ArrayUtils::L2InnerProduct(p.getV()); + double e_kin = 0.0; + singleIterator([&e_kin](const Particle& p) { + e_kin += 0.5 * p.getM() * ArrayUtils::L2InnerProduct(p.getV()); }); - return E_kin; + return e_kin; } void DirectSumContainer::imposeInvariant() { SPDLOG_TRACE("DirectSumContainer::imposeInvariant()"); } + +ivec3 DirectSumContainer::getDomain() { + SPDLOG_TRACE("DirectSumContainer::getDomain()"); + return {-1, -1, -1}; +} + +void DirectSumContainer::computeInteractiveForcesC18( + const std::vector>& interactive_forces) { + SpdWrapper::get()->warn( + "DirectSumContainer does not parallelize and does not use C18"); + for (size_t i = 0; i < particles_.size(); ++i) { + dvec3 force_accum = {0.0, 0.0, 0.0}; + for (size_t j = i + 1; j < particles_.size(); ++j) { + dvec3 f12 = {0.0, 0.0, 0.0}; + for (auto& force : interactive_forces) { + dvec3 ftmp = force->directionalForce(particles_[i], particles_[j]); + f12 = f12 + ftmp; + } + force_accum = force_accum + f12; + particles_[j].subF(f12); + } + particles_[i].addF(force_accum); + } +} diff --git a/src/defs/containers/DirectSumContainer.h b/src/defs/containers/DirectSumContainer.h index 5778c1e4..e938b566 100644 --- a/src/defs/containers/DirectSumContainer.h +++ b/src/defs/containers/DirectSumContainer.h @@ -10,8 +10,7 @@ * Direct sum container class, standard n^2 / 2 newton 3 scheme is applied here */ class DirectSumContainer final : public ParticleContainer { - private: - std::vector particles; + std::vector particles_; public: /** @@ -34,7 +33,7 @@ class DirectSumContainer final : public ParticleContainer { * @brief Add a particle to the container * @param p Particle to be added */ - void addParticle(const Particle& p) override; + void addParticle(const Particle& p); /** * @brief Add a vector of particles to the container @@ -87,5 +86,54 @@ class DirectSumContainer final : public ParticleContainer { void pairIterator( const std::function& f) override; + /** + * @brief does not use parallelization nor c18 coloring, done because of + * pattern and inheritance + * @param interactive_forces + */ + [[deprecated]] void computeInteractiveForcesC18( + const std::vector>& interactive_forces) + override; + + /** + * does not use parallelization, done because of inheritance and pattern + * @param interactive_forces + */ + [[deprecated]] void computeInteractiveForcesForceBuffer( + const std::vector>& interactive_forces) + override { + computeInteractiveForcesC18(interactive_forces); + } + + /** + * @brief calculates the singular forces on all particles + * @param singular_forces singular forces to be iterated over + */ + void computeSingularForces(const std::vector>& + singular_forces) override {} + + /** + * Calculates the kinetic energy of the system 1/2 sum m_i abs(v_i^2) + * @return kinetic energy of the system + */ double getKineticEnergy() override; + + /** + * @brief Get particle count + * @return particle in the container + * @note this is technically unnecessary as its just size + */ + size_t getParticleCount() override { return particles_.size(); } + + /** + * @brief Get fixed particle count + * @return number of fixed particles + */ + size_t getSpecialParticleCount() override { return 0; }; + + /** + * @brief Get simulation domain + * @return domain of the simulation + */ + ivec3 getDomain() override; }; \ No newline at end of file diff --git a/src/defs/containers/LinkedCellsContainer.cpp b/src/defs/containers/LinkedCellsContainer.cpp index b620718c..b1bbb7bb 100644 --- a/src/defs/containers/LinkedCellsContainer.cpp +++ b/src/defs/containers/LinkedCellsContainer.cpp @@ -3,10 +3,15 @@ // #include "LinkedCellsContainer.h" +#ifdef _OPENMP +#include // Needs to stay, clion doesnt recognize flags +#endif #include #include #include #include +#include +#include #include #include "debug/debug_print.h" @@ -19,67 +24,72 @@ const double LinkedCellsContainer::sigma_factor = std::pow(2.0, 1.0 / 6.0); LinkedCellsContainer::LinkedCellsContainer( const LinkedCellsConfig &linked_cells_config) { - domain = linked_cells_config.domain; + domain_ = linked_cells_config.domain; + particle_count_ = 0; + special_particle_count_ = 0; DEBUG_PRINT("LinkedCellsContainer instantiated"); - SpdWrapper::get()->info("domain size: ({}, {}, {})", domain[0], domain[1], - domain[2]); + SpdWrapper::get()->info("domain size: ({}, {}, {})", domain_[0], domain_[1], + domain_[2]); - cells = {}; - this->cutoff = linked_cells_config.cutoff_radius; + cells_ = {}; + this->cutoff_ = linked_cells_config.cutoff_radius; + this->is_membrane_ = linked_cells_config.is_membrane; - cell_count = {std::max(static_cast(std::floor(domain[0] / cutoff)), 1), - std::max(static_cast(std::floor(domain[1] / cutoff)), 1), - std::max(static_cast(std::floor(domain[2] / cutoff)), 1)}; + cell_count_ = { + std::max(static_cast(std::floor(domain_[0] / cutoff_)), 1), + std::max(static_cast(std::floor(domain_[1] / cutoff_)), 1), + std::max(static_cast(std::floor(domain_[2] / cutoff_)), 1)}; - cell_dim = {static_cast(domain[0]) / cell_count[0], - static_cast(domain[1]) / cell_count[1], - static_cast(domain[2]) / cell_count[2]}; + cell_dim_ = {static_cast(domain_[0]) / cell_count_[0], + static_cast(domain_[1]) / cell_count_[1], + static_cast(domain_[2]) / cell_count_[2]}; // safety check that minimum cell count is satisfied so the boundaries work as // expected: - // could be ok, but then reflective calculation has to be disabled, since all + // could be ok, but then periodic calculation has to be disabled, since all // possible pairs are already iterated over for (std::size_t i = 0; i < 2; i++) { - if (cell_count[i] < 3) { - SpdWrapper::get()->info( - "cell count is too small if reflective boundaries are used"); + if (cell_count_[i] < 3) { + SpdWrapper::get()->error( + "Cell count is too small if periodic boundaries are used! If this " + "is not a testing instance, please exit the simulation"); } } // add 2 for halo - cell_count = {cell_count[0] + 2, cell_count[1] + 2, cell_count[2] + 2}; + cell_count_ = {cell_count_[0] + 2, cell_count_[1] + 2, cell_count_[2] + 2}; - cells.resize(cell_count[0] * cell_count[1] * cell_count[2]); + cells_.resize(cell_count_[0] * cell_count_[1] * cell_count_[2]); - this->boundary_config = linked_cells_config.boundary_config; + this->boundary_config_ = linked_cells_config.boundary_config; - DEBUG_PRINT_FMT("Num Cells: {}", cells.size()); + DEBUG_PRINT_FMT("Num Cells: {}", cells_.size()); - halo_direction_cells = {}; - boundary_direction_cells = {}; + halo_direction_cells_ = {}; + boundary_direction_cells_ = {}; // precalculate special cells - for (std::size_t cell_index = 0; cell_index < cells.size(); ++cell_index) { - auto halo_directions = special_cell_direction( + for (std::size_t cell_index = 0; cell_index < cells_.size(); ++cell_index) { + auto halo_directions = specialCellDirection( cell_index, [this](const std::size_t index) { return isHalo(index); }, -1, 2); - auto boundary_directions = special_cell_direction( + auto boundary_directions = specialCellDirection( cell_index, [this](const std::size_t index) { return isBoundary(index); }, 0, 3); if (!halo_directions.empty()) { for (const unsigned long halo_direction : halo_directions) { - halo_direction_cells[halo_direction].push_back(cell_index); + halo_direction_cells_[halo_direction].push_back(cell_index); } } if (!boundary_directions.empty()) { for (const unsigned long boundary_direction : boundary_directions) { - boundary_direction_cells[boundary_direction].push_back(cell_index); + boundary_direction_cells_[boundary_direction].push_back(cell_index); } } } - this->boundaries = { + this->boundaries_ = { linked_cells_config.boundary_config.x_low, linked_cells_config.boundary_config.x_high, linked_cells_config.boundary_config.y_low, @@ -87,9 +97,14 @@ LinkedCellsContainer::LinkedCellsContainer( linked_cells_config.boundary_config.z_low, linked_cells_config.boundary_config.z_high, }; + + is_membrane_ = linked_cells_config.is_membrane; + INFO_FMT("Using membranes is {}", is_membrane_) + SpdWrapper::get()->info("cell dim: {}, {}, {}; cell count: {}, {}, {}", - cell_dim[0], cell_dim[1], cell_dim[2], cell_count[0], - cell_count[1], cell_count[2]); + cell_dim_[0], cell_dim_[1], cell_dim_[2], + cell_count_[0], cell_count_[1], cell_count_[2]); + initializeC18Schema(); } void LinkedCellsContainer::addParticle(const Particle &p) { @@ -98,27 +113,47 @@ void LinkedCellsContainer::addParticle(const Particle &p) { SpdWrapper::get()->error("Tried to add particle out of bounds"); exit(1); } - cells[index].emplace_back(p); + particles_.push_back(p); + cells_[index].push_back(&particles_.back()); + + this->particle_count_++; - DEBUG_PRINT_FMT("Added particle with coords ({}, {}, {}) into cell index: {}", - p.getX()[0], p.getX()[1], p.getX()[2], index) + if (p.getType() < 0) { + this->special_particle_count_++; + } } void LinkedCellsContainer::addParticles( const std::vector &particles) { + particles_.reserve(particles.size()); for (const Particle &p : particles) { addParticle(p); } + + if (is_membrane_) { + SpdWrapper::get()->info("Recalculated the neighbour references"); + setNeighbourReferences(); + } + + SpdWrapper::get()->info("Added new particles"); } void LinkedCellsContainer::removeParticle(const Particle &p) { + SpdWrapper::get()->info("Particle Id remove: {}", p.getId()); const std::size_t index = dvec3ToCellIndex(p.getX()); - std::vector &particles = cells[index]; + std::vector &particles = cells_[index]; particles.erase(std::remove_if(particles.begin(), particles.end(), - [&p](const Particle &q) { return p == q; }), + [&p](const Particle *q) { + return *q == p; + }), // Compare dereferenced Particle* particles.end()); + this->particle_count_--; + if (p.getType() < 0) { + this->special_particle_count_--; + } + DEBUG_PRINT_FMT( "Removed particle with coords ({}, {}, {}) from cell index: {}", p.getX()[0], p.getX()[1], p.getX()[2], index) @@ -139,7 +174,7 @@ std::vector LinkedCellsContainer::getParticlesObjects() { [[nodiscard]] std::size_t LinkedCellsContainer::size() const { std::size_t count = 0; - for (auto &c : cells) { + for (auto &c : cells_) { count += c.size(); } return count; @@ -147,120 +182,55 @@ std::vector LinkedCellsContainer::getParticlesObjects() { void LinkedCellsContainer::imposeInvariant() { // register in corresponding cell - for (std::size_t index = 0; index < cells.size(); index++) { - for (auto it = cells[index].begin(); it < cells[index].end();) { - const std::size_t shouldBeIndex = dvec3ToCellIndex(it->getX()); - if (shouldBeIndex == index) { + for (std::size_t index = 0; index < cells_.size(); index++) { + for (auto it = cells_[index].begin(); it < cells_[index].end();) { + if (*it == nullptr) { + SpdWrapper::get()->error("Nullptr found"); + continue; + } + const std::size_t should_be_index = dvec3ToCellIndex((*it)->getX()); + if (should_be_index == index) { ++it; continue; } + const ivec3 should_be_cell = cellIndexToCoord(should_be_index); - cells[shouldBeIndex].push_back(*it); - it = cells[index].erase(it); + if (!isValidCellCoordinate(should_be_cell)) { + std::cout << (*it)->getX()[0] << ", " << (*it)->getX()[1] << ", " + << (*it)->getX()[2] << std::endl; + } + cells_[should_be_index].push_back(*it); + it = cells_[index].erase(it); } } - // apply boundary condition - // it is assumed that GhostParticles do not have to persist, so we dont have + // it is assumed that GhostParticles do not have to persist, so we don't have // to iterate over the halo cells of Reflective Boundaries - // accessible, 4 instead of 6 - for (size_t dimension = 0; dimension < 4; ++dimension) { - switch (boundaries[dimension]) { + for (size_t dimension = 0; dimension < 6; ++dimension) { + switch (boundaries_[dimension]) { case LinkedCellsConfig::BoundaryType::Outflow: { // clear halo - for (const size_t cell_index : halo_direction_cells[dimension]) { - cells[cell_index].clear(); - cells[cell_index].shrink_to_fit(); + for (const size_t cell_index : halo_direction_cells_[dimension]) { + particle_count_ -= + cells_[cell_index].size(); // update particle count, only place + // where particles are deleted + cells_[cell_index].clear(); + cells_[cell_index].shrink_to_fit(); } break; } case LinkedCellsConfig::BoundaryType::Reflective: { - apply_reflective_boundary(dimension); + applyReflectiveBoundary(dimension); break; } case LinkedCellsConfig::Periodic: { - // move particles in halo to the other side - // calculate forces only for ?_high, so all particles are until then in - // the right place - const std::size_t problematic_dimension = dimension / 2; - const std::size_t problematic_dimension_direction = dimension % 2; - - for (const std::size_t cell_index : halo_direction_cells[dimension]) { - int counter = 0; - for (auto it = cells[cell_index].begin(); - it < cells[cell_index].end(); ++it) { - counter++; - dvec3 new_pos = it->getX(); - new_pos[problematic_dimension] += - domain[problematic_dimension] * - (problematic_dimension_direction % 2 == 0 ? 1 : -1); - const std::size_t shouldBeIndex = dvec3ToCellIndex(new_pos); - - it->setX(new_pos); - cells[shouldBeIndex].push_back(*it); - } - - cells[cell_index].clear(); - cells[cell_index].shrink_to_fit(); - } - - // skip force calculation for lower side of the axis - if (problematic_dimension_direction == 0) { - break; - } - - // iterate over all 9 / 3 cells on the other end - // for now strict 2D implementation for performance - for (const std::size_t cell_index : - boundary_direction_cells[dimension]) { - ivec3 cell_coordinates = cellIndexToCoord(cell_index); - - // change 3 to 9 for 3D - for (std::size_t i = 0; i < 3; ++i) { - ivec3 offset = index_offsets[problematic_dimension][i]; - const ivec3 cell_to_check = cell_coordinates + offset; - bool is_adjacent_cell; - ivec3 adjacent_cell_coordinates; - dvec3 particle_distance_offset; - - std::tie(is_adjacent_cell, adjacent_cell_coordinates, - particle_distance_offset) = - reflective_warp_around(cell_to_check, dimension); - - if (!is_adjacent_cell) { - continue; - } - - const auto adjacent_cell_index = - cellCoordToIndex(adjacent_cell_coordinates); - - // iterate over all pairs and calculate force - for (Particle &p : cells[cell_index]) { - for (Particle &q : cells[adjacent_cell_index]) { - // distance check - const dvec3 accounted_particle_distance = - q.getX() - p.getX() + particle_distance_offset; - - if (ArrayUtils::L2InnerProduct(accounted_particle_distance) >= - cutoff * cutoff) { - continue; - } - - const dvec3 applied_force = - LennardJones::directionalForceWithOffset( - p, q, accounted_particle_distance); - p.setF(p.getF() + applied_force); - q.setF(q.getF() - applied_force); - } - } - } - } + applyPeriodicBoundary(dimension); break; } default: { DEBUG_PRINT_FMT("BoundaryType {} for dimension {} unknown", - static_cast(boundaries[dimension]), dimension); + static_cast(boundaries_[dimension]), dimension); break; } } @@ -269,9 +239,9 @@ void LinkedCellsContainer::imposeInvariant() { void LinkedCellsContainer::singleIterator( const std::function &f) { - for (auto &c : cells) { - for (auto &p : c) { - f(p); + for (const auto &cell : cells_) { + for (const auto &p : cell) { + f(*p); } } } @@ -283,211 +253,413 @@ void LinkedCellsContainer::pairIterator( // - for better cache usage (in flattened version) minimize max distance // between values // - direction of traversal: z, y, x - const std::array offsets = {{ - // 9 x facing - {{1, -1, -1}}, - {{1, -1, 0}}, - {{1, -1, 1}}, - {{1, 0, -1}}, - {{1, 0, 0}}, - {{1, 0, 1}}, - {{1, 1, -1}}, - {{1, 1, 0}}, - {{1, 1, 1}}, - // 3 y - {{0, 1, -1}}, - {{0, 1, 0}}, - {{0, 1, 1}}, - // last z - {{0, 0, 1}}, - }}; - - // go over all cell indices - for (std::size_t cellIndex = 0; cellIndex < cells.size(); cellIndex++) { - std::vector &cellParticles = cells[cellIndex]; - - if (cellParticles.empty()) continue; - - ivec3 cellCoordinate = cellIndexToCoord(cellIndex); + + for (std::size_t cell_index = 0; cell_index < cells_.size(); cell_index++) { + std::vector cell_particles = cells_[cell_index]; + + if (cell_particles.empty()) continue; + + ivec3 cell_coordinate = cellIndexToCoord(cell_index); DEBUG_PRINT_FMT("cell index: {}; coord = ({}, {}, {}); halo? = {}", - cellIndex, cellCoordinate[0], cellCoordinate[1], - cellCoordinate[2], isHalo(cellIndex)); - - // iterate over particles inside cell - for (std::size_t i = 0; i < cellParticles.size(); ++i) { - for (std::size_t j = i + 1; j < cellParticles.size(); ++j) { - const dvec3 p = cellParticles[i].getX(); - const dvec3 q = cellParticles[j].getX(); + cell_index, cell_coordinate[0], cell_coordinate[1], + cell_coordinate[2], isHalo(cell_index)) + + for (std::size_t i = 0; i < cell_particles.size(); ++i) { + for (std::size_t j = i + 1; j < cell_particles.size(); ++j) { + const dvec3 p = cell_particles[i]->getX(); + const dvec3 q = cell_particles[j]->getX(); if (dvec3 d = {p[0] - q[0], p[1] - q[1], p[2] - q[2]}; - d[0] * d[0] + d[1] * d[1] + d[2] * d[2] > cutoff * cutoff) + d[0] * d[0] + d[1] * d[1] + d[2] * d[2] > cutoff_ * cutoff_) { continue; - f(cellParticles[i], cellParticles[j]); - DEBUG_PRINT_FMT("Intra cell pair: ({}, {})", cellParticles[i].getType(), - cellParticles[j].getType()); + } + f(*cell_particles[i], *cell_particles[j]); + DEBUG_PRINT_FMT("Intra cell pair: ({}, {})", cell_particles[i]->getId(), + cell_particles[j]->getId()); } } // iterate over neighbouring particles - for (auto &offset : offsets) { + for (auto &offset : no3_offsets_) { // compute neighbourIndex and check if it is valid - const ivec3 neighbourCoord = {cellCoordinate[0] + offset[0], - cellCoordinate[1] + offset[1], - cellCoordinate[2] + offset[2]}; + const ivec3 neighbour_coord = {cell_coordinate[0] + offset[0], + cell_coordinate[1] + offset[1], + cell_coordinate[2] + offset[2]}; - if (!isValidCellCoordinate(neighbourCoord)) { - DEBUG_PRINT_FMT("Invalid coord: ({}, {}, {})", neighbourCoord[0], - neighbourCoord[1], neighbourCoord[2]) + if (!isValidCellCoordinate(neighbour_coord)) { + DEBUG_PRINT_FMT("Invalid coord: ({}, {}, {})", neighbour_coord[0], + neighbour_coord[1], neighbour_coord[2]) continue; } - const size_t neighbourIndex = cellCoordToIndex(neighbourCoord); + const size_t neighbour_index = cellCoordToIndex(neighbour_coord); DEBUG_PRINT_FMT( "Checking cell i={}; c=({}, {}, {}) for pairs (offset = ({}, {}, " "{}))", - neighbourIndex, neighbourCoord[0], neighbourCoord[1], - neighbourCoord[2], offset[0], offset[1], offset[2]); + neighbour_index, neighbour_coord[0], neighbour_coord[1], + neighbour_coord[2], offset[0], offset[1], offset[2]); + + std::vector &neighbour_particles = cells_[neighbour_index]; + if (neighbour_particles.empty()) continue; + + for (const auto cell_particle : cell_particles) { + for (const auto neighbour_particle : neighbour_particles) { + auto p = cell_particle->getX(); + auto q = neighbour_particle->getX(); + + if (dvec3 d = {p[0] - q[0], p[1] - q[1], p[2] - q[2]}; + d[0] * d[0] + d[1] * d[1] + d[2] * d[2] > cutoff_ * cutoff_) + continue; + + f(*cell_particle, *neighbour_particle); + DEBUG_PRINT_FMT("Cross cell pair: ({}, {})", cell_particle->getType(), + neighbour_particle->getType()) + } + } + } + } +} + +void LinkedCellsContainer::computeInteractiveForcesForceBuffer( + const std::vector> &interactive_forces) { + // parallelization type 1: Force buffers +#ifdef _OPENMP + int num_threads = omp_get_max_threads(); +#else + int num_threads = 1; +#endif + std::vector> force_buffers( + num_threads, std::vector(particles_.size(), {0, 0, 0})); +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic) +#endif + for (std::size_t cell_index = 0; cell_index < cells_.size(); cell_index++) { + std::vector &cell_particles = cells_[cell_index]; + if (cell_particles.empty()) continue; +#ifdef _OPENMP + int thread_id = omp_get_thread_num(); +#else + int thread_id = 0; +#endif + std::vector &force_buffer = force_buffers[thread_id]; + ivec3 cell_coordinate = cellIndexToCoord(cell_index); + DEBUG_PRINT_FMT("cell index: {}; coord = ({}, {}, {}); halo? = {}", + cell_index, cell_coordinate[0], cell_coordinate[1], + cell_coordinate[2], isHalo(cell_index)) + + for (std::size_t i = 0; i < cell_particles.size(); ++i) { + for (std::size_t j = i + 1; j < cell_particles.size(); ++j) { + const dvec3 p = cell_particles[i]->getX(); + const dvec3 q = cell_particles[j]->getX(); + if (dvec3 d = {p[0] - q[0], p[1] - q[1], p[2] - q[2]}; + d[0] * d[0] + d[1] * d[1] + d[2] * d[2] > cutoff_ * cutoff_) + continue; + + dvec3 f12 = {0, 0, 0}; + for (auto &force : interactive_forces) { + dvec3 ftmp = + force->directionalForce(*cell_particles[i], *cell_particles[j]); + f12 = f12 + ftmp; + } + + force_buffer[cell_particles[i]->getId()] = + force_buffer[cell_particles[i]->getId()] + f12; + force_buffer[cell_particles[j]->getId()] = + force_buffer[cell_particles[j]->getId()] - f12; + } + } + + // iterate over neighbouring particles + for (auto &offset : no3_offsets_) { + // compute neighbourIndex and check if it is valid + const ivec3 neighbour_coord = {cell_coordinate[0] + offset[0], + cell_coordinate[1] + offset[1], + cell_coordinate[2] + offset[2]}; + + if (!isValidCellCoordinate(neighbour_coord)) { + DEBUG_PRINT_FMT("Invalid coord: ({}, {}, {})", neighbour_coord[0], + neighbour_coord[1], neighbour_coord[2]) + continue; + } + + const size_t neighbour_index = cellCoordToIndex(neighbour_coord); + DEBUG_PRINT_FMT( + "Checking cell i={}; c=({}, {}, {}) for pairs (offset = ({}, {}, " + "{}))", + neighbour_index, neighbour_coord[0], neighbour_coord[1], + neighbour_coord[2], offset[0], offset[1], offset[2]); // go over all pairs with neighbour particles - std::vector &neighbourParticles = cells[neighbourIndex]; - if (neighbourParticles.empty()) continue; + std::vector &neighbour_particles = cells_[neighbour_index]; + if (neighbour_particles.empty()) continue; - for (auto &cellParticle : cellParticles) { - for (auto &neighbourParticle : neighbourParticles) { - auto p = cellParticle.getX(); - auto q = neighbourParticle.getX(); + for (const auto &cell_particle : cell_particles) { + for (const auto &neighbour_particle : neighbour_particles) { + auto p = cell_particle->getX(); + auto q = neighbour_particle->getX(); if (dvec3 d = {p[0] - q[0], p[1] - q[1], p[2] - q[2]}; - d[0] * d[0] + d[1] * d[1] + d[2] * d[2] > cutoff * cutoff) + d[0] * d[0] + d[1] * d[1] + d[2] * d[2] > cutoff_ * cutoff_) continue; - f(cellParticle, neighbourParticle); - DEBUG_PRINT_FMT("Cross cell pair: ({}, {})", cellParticle.getType(), - neighbourParticle.getType()) + dvec3 f12 = {0, 0, 0}; + for (auto &force : interactive_forces) { + f12 = f12 + + force->directionalForce(*cell_particle, *neighbour_particle); + } + + force_buffer[cell_particle->getId()] = + force_buffer[cell_particle->getId()] + f12; + force_buffer[neighbour_particle->getId()] = + force_buffer[neighbour_particle->getId()] - f12; } } } } +#ifdef _OPENMP +#pragma omp barrier +#endif + + // for some reason parallelising this makes it slower, no matter which + // approach is used + for (size_t j = 0; j < particle_count_; j++) { + for (int i = 1; i < num_threads; i++) { + { + force_buffers[0][j] = force_buffers[0][j] + force_buffers[i][j]; + } + } + } + +#pragma omp parallel for + for (size_t i = 0; i < particles_.size(); ++i) { + Particle &p = particles_[i]; + dvec3 f = force_buffers[0][p.getId()]; + p.addF(f); + } +#pragma omp barrier } void LinkedCellsContainer::boundaryIterator( const std::function &f) { - for (std::size_t index = 0; index < cells.size(); index++) { + for (std::size_t index = 0; index < cells_.size(); index++) { if (!isBoundary(index)) continue; - - for (auto &p : cells[index]) { - f(p); + for (const auto &p : cells_[index]) { + f(*p); } } } void LinkedCellsContainer::haloIterator( const std::function &f) { - for (std::size_t index = 0; index < cells.size(); index++) { + for (std::size_t index = 0; index < cells_.size(); index++) { if (!isHalo(index)) continue; + for (const auto &p : cells_[index]) { + f(*p); + } + } +} + +void LinkedCellsContainer::computeInteractiveForcesC18( + const std::vector> &interactive_forces) { + for (auto &colour : c18_colours_) { +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic) +#endif + for (const std::size_t cell_index : colour) { + if (cells_[cell_index].empty()) { + continue; + } + ivec3 cell_coordinate = cellIndexToCoord(cell_index); + auto &cell = cells_[cell_index]; + + // iterate over particles inside cell + for (std::size_t p1 = 0; p1 < cell.size(); p1++) { + const auto p = cell[p1]; + dvec3 force_accum = {0, 0, 0}; + for (std::size_t p2 = p1 + 1; p2 < cell.size(); p2++) { + const auto q = cell[p2]; + const dvec3 p_x = p->getX(); + const dvec3 q_x = q->getX(); + if (dvec3 d = {p_x[0] - q_x[0], p_x[1] - q_x[1], p_x[2] - q_x[2]}; + d[0] * d[0] + d[1] * d[1] + d[2] * d[2] > cutoff_ * cutoff_) + continue; + dvec3 neighbour_force = {0, 0, 0}; + for (auto &force : interactive_forces) { + neighbour_force = neighbour_force + force->directionalForce(*p, *q); + } + force_accum = force_accum + neighbour_force; + q->subF(neighbour_force); + DEBUG_PRINT_FMT("Intra cell pair: ({}, {})", p->getType(), + q->getType()); + } + p->addF(force_accum); + } + + // iterate over neighbouring newton3 cells + for (auto &offset : no3_offsets_) { + // compute neighbourCoord and check if it is valid + const ivec3 neighbour_coord = {cell_coordinate[0] + offset[0], + cell_coordinate[1] + offset[1], + cell_coordinate[2] + offset[2]}; + + if (!isValidCellCoordinate(neighbour_coord)) { + DEBUG_PRINT_FMT("Invalid coord: ({}, {}, {})", neighbour_coord[0], + neighbour_coord[1], neighbour_coord[2]) + continue; + } - for (auto &p : cells[index]) { - f(p); + const size_t neighbourIndex = cellCoordToIndex(neighbour_coord); + DEBUG_PRINT_FMT( + "Checking cell i={}; c=({}, {}, {}) for pairs (offset = ({}, {}, " + "{}))", + neighbourIndex, neighbour_coord[0], neighbour_coord[1], + neighbour_coord[2], offset[0], offset[1], offset[2]); + + // go over all pairs with neighbour particles + auto &neighbour_particles = cells_[neighbourIndex]; + if (neighbour_particles.empty()) continue; + + for (const auto &cell_particle : cell) { + dvec3 force_accum = {0, 0, 0}; + for (const auto &neighbour_particle : neighbour_particles) { + auto p = cell_particle->getX(); + auto q = neighbour_particle->getX(); + + if (dvec3 d = {p[0] - q[0], p[1] - q[1], p[2] - q[2]}; + d[0] * d[0] + d[1] * d[1] + d[2] * d[2] > cutoff_ * cutoff_) + continue; + dvec3 neighbour_force = {0, 0, 0}; + for (auto &force : interactive_forces) { + neighbour_force = + neighbour_force + + force->directionalForce(*cell_particle, *neighbour_particle); + } + force_accum = force_accum + neighbour_force; + neighbour_particle->subF(neighbour_force); + DEBUG_PRINT_FMT("Cross cell pair: ({}, {})", cell_particle->getId(), + neighbour_particle->getId()) + } + cell_particle->addF(force_accum); + } + } } +#ifdef _OPENMP +#pragma omp barrier +#endif + } +} + +void LinkedCellsContainer::computeSingularForces( + const std::vector> &singular_forces) { +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic) +#endif + for (auto &p : particles_) { + dvec3 f = {0, 0, 0}; + for (auto &force : singular_forces) { + f = f + force->applyForce(p); + } + p.addF(f); } } inline std::size_t LinkedCellsContainer::dvec3ToCellIndex( const dvec3 &position) const { - const std::array cellCoords = { - static_cast(std::floor(position[0] / cell_dim[0])), - static_cast(std::floor(position[1] / cell_dim[1])), - static_cast(std::floor(position[2] / cell_dim[2]))}; + const std::array cell_coords = { + static_cast(std::floor(position[0] / cell_dim_[0])), + static_cast(std::floor(position[1] / cell_dim_[1])), + static_cast(std::floor(position[2] / cell_dim_[2]))}; - return cellCoordToIndex(cellCoords); + return cellCoordToIndex(cell_coords); } inline std::size_t LinkedCellsContainer::cellCoordToIndex( const ivec3 position) const { - return (position[0] + 1) * (cell_count[1] * cell_count[2]) + - (position[1] + 1) * (cell_count[2]) + (position[2] + 1); + return (position[0] + 1) * (cell_count_[1] * cell_count_[2]) + + (position[1] + 1) * (cell_count_[2]) + (position[2] + 1); } inline ivec3 LinkedCellsContainer::cellIndexToCoord( - std::size_t cellIndex) const { - const int x = static_cast(cellIndex / (cell_count[1] * cell_count[2])); - cellIndex = cellIndex - (x * cell_count[1] * cell_count[2]); + std::size_t cell_index) const { + const int x = + static_cast(cell_index / (cell_count_[1] * cell_count_[2])); + cell_index = cell_index - (x * cell_count_[1] * cell_count_[2]); - const int y = static_cast(cellIndex / cell_count[2]); - const int z = static_cast(cellIndex - (y * cell_count[2])); + const int y = static_cast(cell_index / cell_count_[2]); + const int z = static_cast(cell_index - (y * cell_count_[2])); return {x - 1, y - 1, z - 1}; } inline bool LinkedCellsContainer::isValidCellCoordinate( const ivec3 coordinate) const { - return (-1 <= coordinate[0] && coordinate[0] <= (cell_count[0] - 2)) && - (-1 <= coordinate[1] && coordinate[1] <= (cell_count[1] - 2)) && - (-1 <= coordinate[2] && coordinate[2] <= (cell_count[2] - 2)); + return (-1 <= coordinate[0] && coordinate[0] <= (cell_count_[0] - 2)) && + (-1 <= coordinate[1] && coordinate[1] <= (cell_count_[1] - 2)) && + (-1 <= coordinate[2] && coordinate[2] <= (cell_count_[2] - 2)); } -inline bool LinkedCellsContainer::isHalo(const ivec3 cellCoord) const { - return cellCoord[0] == -1 || cellCoord[1] == -1 || cellCoord[2] == -1 || - cellCoord[0] == (cell_count[0] - 2) || - cellCoord[1] == (cell_count[1] - 2) || - cellCoord[2] == (cell_count[2] - 2); +inline bool LinkedCellsContainer::isHalo(const ivec3 cell_coord) const { + return cell_coord[0] == -1 || cell_coord[1] == -1 || cell_coord[2] == -1 || + cell_coord[0] == (cell_count_[0] - 2) || + cell_coord[1] == (cell_count_[1] - 2) || + cell_coord[2] == (cell_count_[2] - 2); } -inline bool LinkedCellsContainer::isHalo(const std::size_t cellIndex) const { - const ivec3 cellCoord = cellIndexToCoord(cellIndex); - return isHalo(cellCoord); +inline bool LinkedCellsContainer::isHalo(const std::size_t cell_index) const { + const ivec3 cell_coord = cellIndexToCoord(cell_index); + return isHalo(cell_coord); } -inline bool LinkedCellsContainer::isBoundary(const ivec3 cellCoord) const { - return (cellCoord[0] == 0 || cellCoord[1] == 0 || cellCoord[2] == 0 || - cellCoord[0] == (cell_count[0] - 3) || - cellCoord[1] == (cell_count[1] - 3) || - cellCoord[2] == (cell_count[2] - 3)) && - !isHalo(cellCoord); +inline bool LinkedCellsContainer::isBoundary(const ivec3 cell_coord) const { + return (cell_coord[0] == 0 || cell_coord[1] == 0 || cell_coord[2] == 0 || + cell_coord[0] == (cell_count_[0] - 3) || + cell_coord[1] == (cell_count_[1] - 3) || + cell_coord[2] == (cell_count_[2] - 3)) && + !isHalo(cell_coord); } inline bool LinkedCellsContainer::isBoundary( - const std::size_t cellIndex) const { - const ivec3 cellCoord = cellIndexToCoord(cellIndex); - return isBoundary(cellCoord); + const std::size_t cell_index) const { + const ivec3 cell_coord = cellIndexToCoord(cell_index); + return isBoundary(cell_coord); } -std::vector LinkedCellsContainer::special_cell_direction( - const std::size_t cellIndex, const std::function &f, - const int lowerMagicNumber, const int upperMagicNumber) const { - if (!f(cellIndex)) return {}; +std::vector LinkedCellsContainer::specialCellDirection( + const std::size_t cell_index, const std::function &f, + const int lower_magic_number, const int upper_magic_number) const { + if (!f(cell_index)) return {}; std::vector directions = {}; - const ivec3 cellCoord = cellIndexToCoord(cellIndex); + const ivec3 cell_coord = cellIndexToCoord(cell_index); - if (cellCoord[0] == lowerMagicNumber) { + if (cell_coord[0] == lower_magic_number) { directions.push_back(xlow); // west } - if (cellCoord[0] == (cell_count[0] - upperMagicNumber)) { + if (cell_coord[0] == (cell_count_[0] - upper_magic_number)) { directions.push_back(xhigh); // east } - if (cellCoord[1] == lowerMagicNumber) { + if (cell_coord[1] == lower_magic_number) { directions.push_back(ylow); // down } - if (cellCoord[1] == (cell_count[1] - upperMagicNumber)) { + if (cell_coord[1] == (cell_count_[1] - upper_magic_number)) { directions.push_back(yhigh); // up } - if (cellCoord[2] == lowerMagicNumber) { + if (cell_coord[2] == lower_magic_number) { directions.push_back(zlow); // south } - if (cellCoord[2] == (cell_count[2] - upperMagicNumber)) { + if (cell_coord[2] == (cell_count_[2] - upper_magic_number)) { directions.push_back(zhigh); // north } return directions; } -bool LinkedCellsContainer::isBoundary_testing( - const std::size_t cellIndex) const { - return isBoundary(cellIndex); +bool LinkedCellsContainer::isBoundaryTesting( + const std::size_t cell_index) const { + return isBoundary(cell_index); } -bool LinkedCellsContainer::isHalo_testing(const std::size_t cellIndex) const { - return isHalo(cellIndex); +bool LinkedCellsContainer::isHaloTesting(const std::size_t cell_index) const { + return isHalo(cell_index); } std::size_t LinkedCellsContainer::dvec3ToCellIndex_testing( @@ -495,27 +667,27 @@ std::size_t LinkedCellsContainer::dvec3ToCellIndex_testing( return dvec3ToCellIndex(position); } -void LinkedCellsContainer::apply_reflective_boundary(const size_t dimension) { +void LinkedCellsContainer::applyReflectiveBoundary(const size_t dimension) { const std::size_t problematic_dimension = dimension / 2; const std::size_t problematic_dimension_direction = dimension % 2; // ensure that GhostParticle only interacts with specific particle // assumed: epsilon and sigma are the same as of the problematic // Particle, the cutoff is larger than half of sigma_factor * sigma - for (const std::size_t cell_index : boundary_direction_cells[dimension]) { - for (Particle &p : cells[cell_index]) { + + for (const std::size_t cell_index : boundary_direction_cells_[dimension]) { + for (const auto p : cells_[cell_index]) { // check if it is too close - double pos = p.getX()[problematic_dimension]; - const double boundary_position = domain[problematic_dimension]; + double pos = p->getX()[problematic_dimension]; + const double boundary_position = domain_[problematic_dimension]; const double double_dist_to_boundary = 2 * std::min(pos, boundary_position - pos); // if both of them are so small that // they would trigger the boundary, the // simulation itself is already broken - - if (double_dist_to_boundary < sigma_factor * p.getSigma()) { + if (double_dist_to_boundary < sigma_factor * p->getSigma()) { const double force = - LennardJones::simpleForce(p, double_dist_to_boundary); - dvec3 p_force = p.getF(); + LennardJones::simpleForce(*p, double_dist_to_boundary); + dvec3 p_force = p->getF(); p_force[problematic_dimension] += force * std::pow(-1.0, @@ -525,42 +697,114 @@ void LinkedCellsContainer::apply_reflective_boundary(const size_t dimension) { // ascending // coordinate // direction - p.setF(p_force); + p->setF(p_force); DEBUG_PRINT_FMT( "Applied Force=[{}, {}, {}] to Particle at [{}, {}, {}]", - p.getF()[0], p.getF()[1], p.getF()[2], p.getX()[0], p.getX()[1], - p.getX()[2]); + p->getF()[0], p->getF()[1], p->getF()[2], p->getX()[0], + p->getX()[1], p->getX()[2]); } } } } -std::tuple LinkedCellsContainer::reflective_warp_around( +void LinkedCellsContainer::applyPeriodicBoundary(const size_t dimension) { + // move particles in halo to the other side + // calculate forces only for ?_high, so all particles are until then in + // the right place + const std::size_t problematic_dimension = dimension / 2; + const std::size_t problematic_dimension_direction = dimension % 2; + + for (const std::size_t cell_index : halo_direction_cells_[dimension]) { + int counter = 0; + for (auto it = cells_[cell_index].begin(); it < cells_[cell_index].end(); + ++it) { + counter++; + dvec3 new_pos = (*it)->getX(); + new_pos[problematic_dimension] += + domain_[problematic_dimension] * + (problematic_dimension_direction % 2 == 0 ? 1 : -1); + const std::size_t should_be_index = dvec3ToCellIndex(new_pos); + + (*it)->setX(new_pos); + cells_[should_be_index].push_back(*it); + } + { + cells_[cell_index].clear(); + cells_[cell_index].shrink_to_fit(); + } + } + + // skip force calculation for lower side of the axis + if (problematic_dimension_direction == 0) { + return; + } + + // iterate over all 9 / 3 cells on the other end + for (const std::size_t cell_index : boundary_direction_cells_[dimension]) { + ivec3 cell_coordinates = cellIndexToCoord(cell_index); + + // change 3 to 9 for 3D + for (std::size_t i = 0; i < 9; ++i) { + ivec3 offset = index_offsets_[problematic_dimension][i]; + const ivec3 cell_to_check = cell_coordinates + offset; + bool is_adjacent_cell; + ivec3 adjacent_cell_coordinates; + dvec3 particle_distance_offset; + + std::tie(is_adjacent_cell, adjacent_cell_coordinates, + particle_distance_offset) = + reflectiveWarpAround(cell_to_check, dimension); + + if (!is_adjacent_cell) { + continue; + } + + const auto adjacent_cell_index = + cellCoordToIndex(adjacent_cell_coordinates); + + // iterate over all pairs and calculate force + + for (const auto &p : cells_[cell_index]) { + for (const auto &q : cells_[adjacent_cell_index]) { + // distance check + const dvec3 accounted_particle_distance = + q->getX() - p->getX() + particle_distance_offset; + + if (ArrayUtils::L2InnerProduct(accounted_particle_distance) >= + cutoff_ * cutoff_) { + continue; + } + + const dvec3 applied_force = LennardJones::directionalForceWithOffset( + *p, *q, accounted_particle_distance); + p->addF(applied_force); + + q->subF(applied_force); + } + } + } + } +} + +std::tuple LinkedCellsContainer::reflectiveWarpAround( const ivec3 cell_coordinate, const std::size_t raw_dimension) const { dvec3 offset = {0, 0, 0}; - if (raw_dimension == yhigh && - boundaries[xlow] == LinkedCellsConfig::Periodic && - boundaries[ylow] == LinkedCellsConfig::Periodic && - (cell_coordinate[0] == -1 || cell_coordinate[0] == cell_count[1] - 2)) { - // both dimensions are periodic -> make sure that corner cells are valid - // only once! skip this for the y dimension, since it was already calculated - // in the x dimension - - SpdWrapper::get()->info("cell should not be warped"); + if (isDoubleCorner(cell_coordinate, raw_dimension)) { + DEBUG_PRINT("cell should not be warped"); return std::make_tuple(false, cell_coordinate, offset); } ivec3 new_cell_coordinate = cell_coordinate; - for (std::size_t dimension = 0; dimension < 2; dimension++) { + for (std::size_t dimension = 0; dimension < 3; dimension++) { if (cell_coordinate[dimension] == -1) { // low wrap around to high cell - new_cell_coordinate[dimension] = cell_count[dimension] - 3; // top cell - offset[dimension] = -domain[dimension]; - } else if (cell_coordinate[dimension] == cell_count[dimension] - 2) { + new_cell_coordinate[dimension] = cell_count_[dimension] - 3; // top cell + offset[dimension] = -domain_[dimension]; + } else if (cell_coordinate[dimension] == cell_count_[dimension] - 2) { // high warp around to low cell new_cell_coordinate[dimension] = 0; // bottom cell - offset[dimension] = domain[dimension]; + offset[dimension] = domain_[dimension]; } else { // no warp around, nothing can go wrong continue; @@ -568,7 +812,7 @@ std::tuple LinkedCellsContainer::reflective_warp_around( // if it is wrapped around but dimension is not periodic, this cell is not // adjacent - if (boundaries[2 * dimension] != LinkedCellsConfig::Periodic) { + if (boundaries_[2 * dimension] != LinkedCellsConfig::Periodic) { return std::make_tuple(false, cell_coordinate, offset); } } @@ -581,25 +825,111 @@ std::tuple LinkedCellsContainer::reflective_warp_around( } std::tuple -LinkedCellsContainer::reflective_warp_around_testing( +LinkedCellsContainer::reflectiveWarpAroundTesting( const ivec3 cell_coordinate, const std::size_t raw_dimension) const { - return reflective_warp_around(cell_coordinate, raw_dimension); + return reflectiveWarpAround(cell_coordinate, raw_dimension); } -std::size_t LinkedCellsContainer::cellCoordToIndex_testing( +std::size_t LinkedCellsContainer::cellCoordToIndexTesting( const ivec3 position) const { return cellCoordToIndex(position); } -ivec3 LinkedCellsContainer::cellIndexToCoord_testing( - const std::size_t cellIndex) const { - return cellIndexToCoord(cellIndex); +ivec3 LinkedCellsContainer::cellIndexToCoordTesting( + const std::size_t cell_index) const { + return cellIndexToCoord(cell_index); } double LinkedCellsContainer::getKineticEnergy() { - double E_kin = 0.0; - singleIterator([&E_kin](const Particle &p) { - E_kin += p.getM() * ArrayUtils::L2InnerProduct(p.getV()); + double e_kin = 0.0; + singleIterator([&e_kin](const Particle &p) { + e_kin += p.getM() * ArrayUtils::L2InnerProduct(p.getV()); }); - return E_kin * 0.5; + return e_kin * 0.5; +} + +bool LinkedCellsContainer::isDoubleCorner( + const ivec3 cell_coordinate, const std::size_t raw_dimension) const { + // check whether it really is a corner + int edge_of_x_dimensions_counter = 0; + for (std::size_t dimension = 0; dimension < 3; dimension++) { + if (cell_coordinate[dimension] == -1 || + cell_coordinate[dimension] == cell_count_[dimension] - 2) { + edge_of_x_dimensions_counter++; + } + } + + if (edge_of_x_dimensions_counter < 2) { + return false; + } + + if (boundaries_[xlow] == LinkedCellsConfig::Periodic && + boundaries_[ylow] == LinkedCellsConfig::Periodic && + boundaries_[zlow] != LinkedCellsConfig::Periodic) { + // 110 + if (raw_dimension == yhigh && (cell_coordinate[0] == -1 || + cell_coordinate[0] == cell_count_[0] - 2)) { + return true; + } + return false; + } + if (boundaries_[xlow] == LinkedCellsConfig::Periodic && + boundaries_[ylow] != LinkedCellsConfig::Periodic && + boundaries_[zlow] == LinkedCellsConfig::Periodic) { + // 101 + if (raw_dimension == zhigh && (cell_coordinate[0] == -1 || + cell_coordinate[0] == cell_count_[0] - 2)) { + return true; + } + return false; + } + if (boundaries_[xlow] != LinkedCellsConfig::Periodic && + boundaries_[ylow] == LinkedCellsConfig::Periodic && + boundaries_[zlow] == LinkedCellsConfig::Periodic) { + // 011 + if (raw_dimension == zhigh && (cell_coordinate[1] == -1 || + cell_coordinate[1] == cell_count_[1] - 2)) { + return true; + } + return false; + } + if (boundaries_[xlow] == LinkedCellsConfig::Periodic && + boundaries_[ylow] == LinkedCellsConfig::Periodic && + boundaries_[zlow] == LinkedCellsConfig::Periodic) { + // 111 + if (raw_dimension == yhigh && (cell_coordinate[0] == -1 || + cell_coordinate[0] == cell_count_[0] - 2)) { + return true; + } + if (raw_dimension == zhigh && (cell_coordinate[0] == -1 || + cell_coordinate[0] == cell_count_[0] - 2)) { + return true; + } + if (raw_dimension == zhigh && (cell_coordinate[1] == -1 || + cell_coordinate[1] == cell_count_[1] - 2)) { + return true; + } + return false; + } + return false; +} + +void LinkedCellsContainer::setNeighbourReferences() { + for (Particle *p : getParticles()) { + std::vector> new_neighbours{}; + + for (Particle *p2 : getParticles()) { + for (auto [diag, ref] : p->getNeighbours()) { + auto *new_p = reinterpret_cast(ref); + + if (p2->getId() == new_p->getId()) { + auto *pointer = (size_t *)p2; + new_neighbours.emplace_back(diag, (size_t)pointer); + } + } + } + + p->resetNeighbours(); + p->setNeighbours(new_neighbours); + } } diff --git a/src/defs/containers/LinkedCellsContainer.h b/src/defs/containers/LinkedCellsContainer.h index 24d8539a..ed111cda 100644 --- a/src/defs/containers/LinkedCellsContainer.h +++ b/src/defs/containers/LinkedCellsContainer.h @@ -6,6 +6,7 @@ #include "defs/Particle.h" #include "defs/Simulation.h" #include "defs/containers/ParticleContainer.h" +#include "forces/InteractiveForce.h" /** * @brief a particle container with linked cells @@ -21,70 +22,60 @@ class LinkedCellsContainer final : public ParticleContainer { * * position = x * (cellsY * cellsZ) + y * (cellsZ) + z */ - std::vector> cells; /** - * @brief - * stores the indexes of all halo_cells for faster iteration in the - * corresponding direction vector - */ - std::array, 6> halo_direction_cells; - - /** - * @brief - * stores the indexes of all halo_cells for faster iteration in the - * corresponding direction vector + * Vector where particles are stored + * @note quasi immutable, particles are just ignored, but not removed */ - std::array, 6> boundary_direction_cells; + std::vector particles_; /** - * @brief - * a more processing friendly storage of LinkedCellsConfig::BoundaryConfig + * Mapping in which cell which particles are */ - std::array boundaries{}; + std::vector> cells_; /** - * @brief - * number of cells per direction for domain + 2 halo cells + * saves the index that we have to locate in particles, so e.g. left or top */ - ivec3 cell_count{}; + std::vector> c18_colours_; /** - * @brief - * cell dimensions + * Colours for C18 schema */ - dvec3 cell_dim{}; + const std::vector c_18_schema_ = { + {-1, -1, -1}, {-1, -1, 0}, {-1, -1, 1}, {-1, 0, -1}, {-1, 0, 0}, + {-1, 0, 1}, {-1, 1, -1}, {-1, 1, 0}, {-1, 1, 1}, {0, -1, -1}, + {0, -1, 0}, {0, -1, 1}, {0, 0, -1}, {0, 0, 0}, {0, 0, 1}, + {0, 1, -1}, {0, 1, 0}, {0, 1, 1}}; /** - * @brief - * cutoff radius + * Offsets for newton 3 scheme */ - double cutoff{}; - - /** - * @brief - * the domain of the container - */ - ivec3 domain{}; - - /** - * @brief - * the boundary config of each direction of the simulation - */ - LinkedCellsConfig::BoundaryConfig boundary_config{}; - - /** - * @brief apply reflective boundary condition to a dimension - * @param dimension the problematic dimension - */ - inline void apply_reflective_boundary(size_t dimension); + const std::array no3_offsets_ = {{ + // 9 x facing + {{1, -1, -1}}, + {{1, -1, 0}}, + {{1, -1, 1}}, + {{1, 0, -1}}, + {{1, 0, 0}}, + {{1, 0, 1}}, + {{1, 1, -1}}, + {{1, 1, 0}}, + {{1, 1, 1}}, + // 3 y + {{0, 1, -1}}, + {{0, 1, 0}}, + {{0, 1, 1}}, + // last z + {{0, 0, 1}}, + }}; + // Maybe move these back for constexpr /** - *@brief index offsets orthogonal to a cell for each dimension, optimized for - *2D simulations - * + * @brief index offsets orthogonal to a cell for each dimension, optimized + * for 2D simulations */ - std::array, 3> index_offsets = {{ + const std::array, 3> index_offsets_ = {{ // x {{{1, 1, 0}, {1, 0, 0}, @@ -119,6 +110,84 @@ class LinkedCellsContainer final : public ParticleContainer { {-1, -1, 1}}}, }}; + /** + * @brief current number of particles + */ + size_t particle_count_{}; + + /** + * @brief number of particles, that are immovable + */ + size_t special_particle_count_{}; + + /** + * @brief enables the neighbour calculation for membranes + */ + bool is_membrane_{}; + + /** + * @brief + * stores the indexes of all halo_cells for faster iteration in the + * corresponding direction vector + */ + std::array, 6> halo_direction_cells_; + + /** + * @brief + * stores the indexes of all halo_cells for faster iteration in the + * corresponding direction vector + */ + std::array, 6> boundary_direction_cells_; + + /** + * @brief + * a more processing friendly storage of LinkedCellsConfig::BoundaryConfig + */ + std::array boundaries_{}; + + /** + * @brief + * number of cells per direction for domain + 2 halo cells + */ + ivec3 cell_count_{}; + + /** + * @brief + * cell dimensions + */ + dvec3 cell_dim_{}; + + /** + * @brief + * cutoff radius + */ + double cutoff_{}; + + /** + * @brief + * the domain of the container + */ + ivec3 domain_{}; + + /** + * @brief + * the boundary config of each direction of the simulation + */ + LinkedCellsConfig::BoundaryConfig boundary_config_{}; + + /** + * @brief apply reflective boundary condition to a dimension + * @param dimension the problematic dimension + */ + inline void applyReflectiveBoundary(size_t dimension); + + /** + * @brief Add a particle to the container + * @param p Particle to be added + * @note Does not impose the invariant automatically! + */ + void addParticle(const Particle& p); + public: /** * 6th root of 2 @@ -142,13 +211,6 @@ class LinkedCellsContainer final : public ParticleContainer { */ ~LinkedCellsContainer() override = default; - /** - * @brief Add a particle to the container - * @param p Particle to be added - * @note Does not impose the invariant automatically! - */ - void addParticle(const Particle& p) override; - /** * @brief Add a vector of particles to the container * @param particles Particles to be added @@ -169,6 +231,20 @@ class LinkedCellsContainer final : public ParticleContainer { */ [[nodiscard]] std::vector getParticles() override; + /** + * @brief the exact number of current particles, updated accordingly + * @return the current count of particles left in the simulation + */ + [[nodiscard]] size_t getParticleCount() const { return particle_count_; } + + /** + * @brief the exact number of current special particles, updated accordingly + * @return the current count of special particles left in the simulation + */ + [[nodiscard]] size_t getSpecialParticleCount() const { + return special_particle_count_; + } + /** * @brief Get a vector of all particles in the container * @return Vector of all particles @@ -181,6 +257,13 @@ class LinkedCellsContainer final : public ParticleContainer { */ [[nodiscard]] std::size_t size() const override; + /** + * applies the periodic boundary conditions to the given dimension + * @param dimension the dimension that the periodic boundary should be applied + * to + */ + inline void applyPeriodicBoundary(size_t dimension); + /** * @brief Impose the invariant, that the particles are spatially sorted into * the correct vectors @@ -203,6 +286,23 @@ class LinkedCellsContainer final : public ParticleContainer { void pairIterator( const std::function& f) override; + /** + * @brief Compute interactive forces using the C18 scheme + no3 + */ + void computeInteractiveForcesC18( + const std::vector>& interactive_forces) + override; + + /** + * @brief Compute interactive forces using the Force Buffer method + * Uses more pragmas than necessary to accommodate single threaded testing + * when openmp is not available + * @param interactive_forces + */ + void computeInteractiveForcesForceBuffer( + const std::vector>& interactive_forces) + override; + /** * @brief Single iterator over all particles in the boundary of the container * @param f Function to be applied @@ -217,17 +317,23 @@ class LinkedCellsContainer final : public ParticleContainer { */ void haloIterator(const std::function& f); + /** + * @brief Compute singular forces + */ + void computeSingularForces(const std::vector>& + singular_forces) override; + /** * @brief Get the amount of cells in each dimension * @return ivec3 of cells in each dimension */ - [[nodiscard]] std::array getCellCount() const { return cell_count; } + [[nodiscard]] std::array getCellCount() const { return cell_count_; } /** - * @brief Get the dimensions of a all cells in the container + * @brief Get the dimensions of all cells in the container * @return dvec3 of the dimensions of all cells */ - [[nodiscard]] dvec3 getCellDim() const { return cell_dim; } + [[nodiscard]] dvec3 getCellDim() const { return cell_dim_; } /** * @brief Gets the cell index of a position @@ -257,21 +363,21 @@ class LinkedCellsContainer final : public ParticleContainer { * @param position Cell coordinate in 3 dimensions * @return Associated cell index */ - [[nodiscard]] std::size_t cellCoordToIndex_testing(ivec3 position) const; + [[nodiscard]] std::size_t cellCoordToIndexTesting(ivec3 position) const; /** * @brief Gets the cell coordinate from the cell index - * @param cellIndex Index of the cell + * @param cell_index Index of the cell * @return Cell coodinate in 3 dimensions */ - [[nodiscard]] inline ivec3 cellIndexToCoord(std::size_t cellIndex) const; + [[nodiscard]] inline ivec3 cellIndexToCoord(std::size_t cell_index) const; /** * @brief API for testing - * @param cellIndex Index of the cell + * @param cell_index Index of the cell * @return Cell coodinate in 3 dimensions */ - [[nodiscard]] ivec3 cellIndexToCoord_testing(std::size_t cellIndex) const; + [[nodiscard]] ivec3 cellIndexToCoordTesting(std::size_t cell_index) const; /** * @brief Checks if a cell coordinate exists in the container @@ -282,45 +388,45 @@ class LinkedCellsContainer final : public ParticleContainer { /** * @brief Checks if a cell coordinate is in the halo of the container - * @param cellCoord Cell coordinate to be checked + * @param cell_coord Cell coordinate to be checked * @return If cell is part of the halo */ - [[nodiscard]] inline bool isHalo(ivec3 cellCoord) const; + [[nodiscard]] inline bool isHalo(ivec3 cell_coord) const; /** * @brief Checks if a cell index is in the halo of the container - * @param cellIndex cell index to be checked + * @param cell_index cell index to be checked * @return If cell is part of the halo */ - [[nodiscard]] inline bool isHalo(std::size_t cellIndex) const; + [[nodiscard]] inline bool isHalo(std::size_t cell_index) const; /** * @brief API for testing - * @param cellIndex cell index to be checked + * @param cell_index cell index to be checked * @return If cell is part of the halo */ - [[nodiscard]] bool isHalo_testing(std::size_t cellIndex) const; + [[nodiscard]] bool isHaloTesting(std::size_t cell_index) const; /** * @brief Checks if a cell coordinate is in the boundary of the container - * @param cellCoord Cell coordinate to be checked + * @param cell_coord Cell coordinate to be checked * @return cell is part of the boundary */ - [[nodiscard]] inline bool isBoundary(ivec3 cellCoord) const; + [[nodiscard]] inline bool isBoundary(ivec3 cell_coord) const; /** * @brief Checks if a cell index is in the boundary of the container - * @param cellIndex cell index to be checked + * @param cell_index cell index to be checked * @return If cell is part of the boundary */ - [[nodiscard]] inline bool isBoundary(std::size_t cellIndex) const; + [[nodiscard]] inline bool isBoundary(std::size_t cell_index) const; /** * @brief API for testing, because gtest does not like inline - * @param cellIndex cell index to be checked + * @param cell_index cell index to be checked * @return If cell is part of the boundary */ - [[nodiscard]] bool isBoundary_testing(std::size_t cellIndex) const; + [[nodiscard]] bool isBoundaryTesting(std::size_t cell_index) const; /** * @brief calculates all directions of the boundary cell @@ -332,21 +438,21 @@ class LinkedCellsContainer final : public ParticleContainer { * 3: up * 4: south * 5: north - * @param cellIndex cell index to be checked + * @param cell_index cell index to be checked * @param f function to check if it is a special cell - * @param lowerMagicNumber lower bound index - * @param upperMagicNumber upper bound index + * @param lower_magic_number lower bound index + * @param upper_magic_number upper bound index * @return the directions of the boundary cell */ - [[nodiscard]] std::vector special_cell_direction( - std::size_t cellIndex, const std::function& f, - int lowerMagicNumber, int upperMagicNumber) const; + [[nodiscard]] std::vector specialCellDirection( + std::size_t cell_index, const std::function& f, + int lower_magic_number, int upper_magic_number) const; /** * @brief Debug method to get direct access to the cells vector * @return Reference to the cell vector */ - std::vector>& getCells() { return cells; } + std::vector>& getCells() { return cells_; } /** * @brief warp negative cell index to maximum cell coordinate to enable @@ -356,7 +462,7 @@ class LinkedCellsContainer final : public ParticleContainer { * @return bool: whether it is a valid cell to be checked, ivec3: real cell, * dvec3: offset to be applied */ - [[nodiscard]] inline std::tuple reflective_warp_around( + [[nodiscard]] inline std::tuple reflectiveWarpAround( ivec3 cell_coordinate, std::size_t raw_dimension) const; /** @@ -366,10 +472,72 @@ class LinkedCellsContainer final : public ParticleContainer { * @return bool: whether it is a valid cell to be checked, ivec3: real cell, * dvec3: offset to be applied */ - [[nodiscard]] std::tuple reflective_warp_around_testing( + [[nodiscard]] std::tuple reflectiveWarpAroundTesting( ivec3 cell_coordinate, std::size_t raw_dimension) const; + /** + * @brief calculates the kinetic energy in the container + * @return kinetic energy of all particles + */ double getKineticEnergy() override; + + /** + * @brief returns particle count of the container + * @return particle count + */ + std::size_t getParticleCount() override { return particle_count_; } + + /** + * @brief returns the count of immovable particles + * @return count of immovable particles + */ + std::size_t getSpecialParticleCount() override { + return special_particle_count_; + } + + /** + * @brief returns the domain of the container + * @return the domain of the container + */ + ivec3 getDomain() override { return domain_; } + + /** + * @brief if true, this corner should not be evaluated because it was already + * done + * @param cell_coordinate the coordinate of the cell + * @param raw_dimension the evaluated dimension + * @return if cell should be ignored + */ + [[nodiscard]] inline bool isDoubleCorner(ivec3 cell_coordinate, + std::size_t raw_dimension) const; + + /** + * @brief since the neighbour references are invalid after adding particles, + * set them again + */ + void setNeighbourReferences(); + + /** + * @brief initializes the C18 schema colours, os their directions, onto the + * cells_ vector. With stride {2,3,3}. + */ + void initializeC18Schema() { + for (auto start_offset : c_18_schema_) { + std::vector iterators; + for (int cx = start_offset[0]; cx <= cell_count_[0]; cx += 2) { + for (int cy = start_offset[1]; cy <= cell_count_[1]; cy += 3) { + for (int cz = start_offset[2]; cz <= cell_count_[2]; cz += 3) { + if (!isValidCellCoordinate({cx, cy, cz})) { + continue; + } + auto cell_index = cellCoordToIndex({cx, cy, cz}); + iterators.push_back(cell_index); + } + } + } + c18_colours_.push_back(iterators); + } + } }; /** diff --git a/src/defs/containers/ParticleContainer.h b/src/defs/containers/ParticleContainer.h index 86017c3a..109cb4e1 100644 --- a/src/defs/containers/ParticleContainer.h +++ b/src/defs/containers/ParticleContainer.h @@ -8,6 +8,8 @@ #include #include "defs/Particle.h" +#include "forces/InteractiveForce.h" +#include "forces/SingularForce.h" /** * @brief Interface for an object storing particles while providing single and @@ -20,15 +22,10 @@ class ParticleContainer { */ virtual ~ParticleContainer() = default; - /** - * @brief Add a particle to the particle system. - * @param p Particle to be added. - */ - virtual void addParticle(const Particle& p) = 0; - virtual void addParticles(const std::vector& particles) = 0; /** * @brief Remove a particle from the particle system. + * DO NOT USE, JUST FOR TESTING! * @param p Particle to be removed. */ virtual void removeParticle(const Particle& p) = 0; @@ -72,5 +69,60 @@ class ParticleContainer { virtual void pairIterator( const std::function& f) = 0; + /** + * @brief Compute interactive forces using the C18 coloring scheme + * (Parallelization strategy 1) + */ + virtual void computeInteractiveForcesC18( + const std::vector>& + interactive_forces) = 0; + + /** + * @brief Compute interactive forces using Force Buffers (Parallalization + * strategy 2) + */ + virtual void computeInteractiveForcesForceBuffer( + const std::vector>& + interactive_forces) = 0; + + /** + * @brief Compute singular forces + */ + virtual void computeSingularForces( + const std::vector>& singular_forces) = 0; + + /** + * @brief Returns the kinetic energy of the system, E_kin = 1/2 \sum_i^n m_i * + * + * @return kinetic energy of the system + */ virtual double getKineticEnergy() = 0; + + /** + * @brief Increment Time for index forces + */ + virtual void incrementTime() { this->current_time++; } + + /** + * Current time for index forces + */ + double current_time = 0; + + /** + * @brief the exact number of current particles, updated accordingly + * @return the current count of particles left in the simulation + */ + [[nodiscard]] virtual size_t getParticleCount() = 0; + + /** + * @brief the exact number of current special particles, updated accordingly + * @return the current count of special particles left in the simulation + */ + [[nodiscard]] virtual size_t getSpecialParticleCount() = 0; + + /** + * @brief returns the domain of the container + * @return the domain of the container + */ + virtual ivec3 getDomain() = 0; }; diff --git a/src/defs/types.h b/src/defs/types.h index ff07f1be..e7019001 100644 --- a/src/defs/types.h +++ b/src/defs/types.h @@ -11,4 +11,5 @@ using dvec3 = std::array; using ivec3 = std::array; +#define POW2_1_6 std::pow(2.0, 1.0 / 6.0) #endif // TYPES_H diff --git a/src/forces/Gravity.cpp b/src/forces/Gravity.cpp index 17491a73..18b8b585 100644 --- a/src/forces/Gravity.cpp +++ b/src/forces/Gravity.cpp @@ -6,8 +6,8 @@ dvec3 Gravity::directionalForce(Particle& p1, Particle& p2) const { const dvec3 r = p2.getX() - p1.getX(); const double dist = ArrayUtils::L2Norm(r); - const double F = (p1.getM() * p2.getM()) / std::pow(dist, 2); - return F / dist * (p2.getX() - p1.getX()); + const double f = (p1.getM() * p2.getM()) / std::pow(dist, 2); + return f / dist * (p2.getX() - p1.getX()); } double Gravity::simpleForce(Particle& p, double distance) { return 0; } diff --git a/src/forces/Gravity.h b/src/forces/Gravity.h index 4b06fcd6..692583e2 100644 --- a/src/forces/Gravity.h +++ b/src/forces/Gravity.h @@ -2,9 +2,9 @@ // Created by jkr on 10/18/24. // #pragma once + #include "InteractiveForce.h" #include "defs/Particle.h" -#include "utils/ArrayUtils.h" /** * @brief Gravitational force F_{1,2} diff --git a/src/forces/HarmonicForce.cpp b/src/forces/HarmonicForce.cpp new file mode 100644 index 00000000..49b9a386 --- /dev/null +++ b/src/forces/HarmonicForce.cpp @@ -0,0 +1,20 @@ +// +// Created by jkr on 12/23/24. +// + +#include "HarmonicForce.h" + +#include "utils/ArrayUtils.h" +#include "utils/SpdWrapper.h" + +dvec3 HarmonicForce::applyForce(const Particle& p) const { + dvec3 force_acc = {0.0, 0.0, 0.0}; + for (auto [is_diagonal, particle_ptr] : p.getNeighbours()) { + const auto* p2 = reinterpret_cast(particle_ptr); + dvec3 rv = p2->getX() - p.getX(); + const double r = ArrayUtils::L2Norm(rv); + dvec3 f = (k_ * (r - (is_diagonal ? sr_0_ : r_0_))) * ((1.0 / r) * rv); + force_acc = force_acc + f; + } + return force_acc; +} diff --git a/src/forces/HarmonicForce.h b/src/forces/HarmonicForce.h new file mode 100644 index 00000000..d493ff56 --- /dev/null +++ b/src/forces/HarmonicForce.h @@ -0,0 +1,40 @@ +// +// Created by jkr on 12/23/24. +// +#ifndef HARMONICFORCE_H +#define HARMONICFORCE_H +#include + +#include "SingularForce.h" + +/** + * @brief Harmonic force for inter particle forces in a membrane + */ +class HarmonicForce final : public SingularForce { + private: + int k_{}; + double r_0_{}; + /** + * sqrt(2) * bond_length == diag. bond length + */ + double sr_0_{}; + + public: + /** + * @brief Instantiates Harmonic Force + * @param k Spring constant + * @param r_0 average bond length + */ + explicit HarmonicForce(const int k, const double r_0) : k_(k), r_0_(r_0) { + sr_0_ = std::sqrt(2) * r_0; + } + + /** + * @brief Calculate force applied by the neighbours + * @param p particle + * @return force vector + */ + [[nodiscard]] dvec3 applyForce(const Particle& p) const override; +}; + +#endif // HARMONICFORCE_H diff --git a/src/forces/IndexForce.cpp b/src/forces/IndexForce.cpp new file mode 100644 index 00000000..6c9d319b --- /dev/null +++ b/src/forces/IndexForce.cpp @@ -0,0 +1,11 @@ +// +// Created by jkr on 12/26/24. +// +#include "IndexForce.h" + +dvec3 IndexForce::applyForce(Particle &p, const double sim_time) const { + if (sim_time < time_) { + return force_values_; + } + return {0.0, 0.0, 0.0}; +} \ No newline at end of file diff --git a/src/forces/IndexForce.h b/src/forces/IndexForce.h new file mode 100644 index 00000000..5d304936 --- /dev/null +++ b/src/forces/IndexForce.h @@ -0,0 +1,57 @@ +// +// Created by jkr on 12/26/24. +// + +#ifndef INDEXFORCE_H +#define INDEXFORCE_H + +#include + +#include "defs/containers/ParticleContainer.h" + +/** + * @brief Holds the Index Force. Index force acts on cuboid coordinates + * specified before the simulation + */ +class IndexForce { + /** + * The particle ids that are ultimately calculated in the membrane generator + */ + std::vector indeces_{}; + /** + * The time until which the force is active + */ + double time_{}; + /** + * The value of the actual force that is then added + */ + dvec3 force_values_{}; + + public: + /** + * @brief Instantiate Index Force + * @param ids Vector of ids, which the force acts upon + * @param time Time until force is applied + * @param force_values force vector which is added to targeted particles + */ + explicit IndexForce(const std::vector& ids, const double time, + const dvec3& force_values) + : indeces_(ids), time_(time), force_values_(force_values) {} + IndexForce() = default; + + /** + * @brief Calculates the index force on the specified particle + * @param p the particle on where the force is applied + * @param sim_time the current time of the simulation + * @return the force vector thats applied (0 if sime_time > time_) + */ + dvec3 applyForce(Particle& p, double sim_time) const; + + /** + * @brief Returns the ids on which the force has to act + * @return the particle ID indices of the force + */ + [[nodiscard]] std::vector getIndices() const { return indeces_; } +}; + +#endif // INDEXFORCE_H diff --git a/src/forces/LennardJones.cpp b/src/forces/LennardJones.cpp index 17916c5d..22f92cf9 100644 --- a/src/forces/LennardJones.cpp +++ b/src/forces/LennardJones.cpp @@ -3,20 +3,20 @@ // #include "LennardJones.h" -#include "utils/ArrayUtils.h" // do not remove this even if clion copes about it +#include "utils/ArrayUtils.h" #include "utils/SpdWrapper.h" dvec3 LennardJones::directionalForce(Particle& p1, Particle& p2) const { - const dvec3 rv = p2.getX() - p1.getX(); - const double r = ArrayUtils::L2Norm(rv); - const double sigma = (p1.getSigma() + p2.getSigma()) / 2; + const dvec3 r = p2.getX() - p1.getX(); + const double r_squared = r[0] * r[0] + r[1] * r[1] + r[2] * r[2]; + const double sigma_squared_div_r_squared = + std::pow(p1.getSigma() + p2.getSigma(), 2) / (4 * r_squared); const double epsilon = std::sqrt(p1.getEpsilon() * p2.getEpsilon()); - const double sr = sigma / r; - const double sr6 = std::pow(sr, 6); - const double sr12 = std::pow(sr6, 2); - const double force_magnitude = - 24 * epsilon * (sr6 - 2 * sr12) / std::pow(r, 2); - return force_magnitude * rv; + const double sr3 = std::pow(sigma_squared_div_r_squared, 3); + const double sr6 = 2 * std::pow(sr3, 2); + const double force_magnitude = 24 * epsilon * (sr3 - sr6) / r_squared; + + return force_magnitude * r; } double LennardJones::simpleForce(const Particle& p, const double distance) { diff --git a/src/forces/SingularForce.h b/src/forces/SingularForce.h index b6c041ce..4488a343 100644 --- a/src/forces/SingularForce.h +++ b/src/forces/SingularForce.h @@ -5,12 +5,19 @@ #define SINGULARFORCE_H #pragma once #include "defs/Particle.h" + /** * @brief instance of forces that are global and not an interaction force */ class SingularForce { public: + /** + * @brief Default constructor of a singular force + */ SingularForce() = default; + /** + * @brief Default destructor of a singular force + */ virtual ~SingularForce() = default; /** diff --git a/src/forces/SingularGravity.cpp b/src/forces/SingularGravity.cpp index 7dbac2f9..ff94ec32 100644 --- a/src/forces/SingularGravity.cpp +++ b/src/forces/SingularGravity.cpp @@ -3,8 +3,11 @@ // #include "SingularGravity.h" -SingularGravity::SingularGravity(const double g) : g(g){}; +SingularGravity::SingularGravity(const double g, const int axis) + : g_(g), axis_(axis){}; dvec3 SingularGravity::applyForce(const Particle& p) const { - return {0.0, g * p.getM(), 0.0}; + dvec3 force{0.0, 0.0, 0.0}; + force[axis_] = g_ * p.getM(); + return force; } \ No newline at end of file diff --git a/src/forces/SingularGravity.h b/src/forces/SingularGravity.h index 53c1414f..5c22b8bf 100644 --- a/src/forces/SingularGravity.h +++ b/src/forces/SingularGravity.h @@ -8,23 +8,28 @@ /** * @brief Globally acts on each particle using the gravitational constant g in - * the y direction + * the specified axis direction [0 : x, 1 : y, 2 : z] */ class SingularGravity final : public SingularForce { private: - double g{}; + /** + * The gravitational scaling constant + */ + double g_{}; + /** + * The axis on which the force is applied {0 : x, 1 : y, 2 : z} + */ + int axis_{}; public: /** - * * @param g the gravitational coefficient + * @param axis the axis 0 - 2 x - z on which gravity acts */ - explicit SingularGravity(double g); + explicit SingularGravity(double g, int axis); - // TODO: do we need to do this modular over the axis? or is it only in y - // direction? /** - * @calculates f_i = p_i.mass * [0,g,0] + * @calculates f_i = p_i.mass * g * 1_{axis} * @param p the particle to act on * @return the force to be added to the particles force */ diff --git a/src/forces/TruncatedLennardJones.cpp b/src/forces/TruncatedLennardJones.cpp new file mode 100644 index 00000000..6c0c084a --- /dev/null +++ b/src/forces/TruncatedLennardJones.cpp @@ -0,0 +1,28 @@ +// +// Created by jkr on 12/25/24. +// + +#include "TruncatedLennardJones.h" + +#include "utils/ArrayUtils.h" +#include "utils/SpdWrapper.h" + +dvec3 TruncatedLennardJones::directionalForce(Particle& p1, + Particle& p2) const { + const dvec3 rv = p2.getX() - p1.getX(); + const double r = ArrayUtils::L2Norm(rv); + const double sigma = (p1.getSigma() + p2.getSigma()) / 2; + if (r >= sigma * 1.22462048309) { + return {0, 0, 0}; + } + const double epsilon = std::sqrt(p1.getEpsilon() * p2.getEpsilon()); + const double sr = sigma / r; + const double sr6 = std::pow(sr, 6); + const double sr12 = std::pow(sr6, 2); + const double force_magnitude = + 24 * epsilon * (sr6 - 2 * sr12) / std::pow(r, 2); + + const dvec3 force = force_magnitude * rv; + + return force; +} diff --git a/src/forces/TruncatedLennardJones.h b/src/forces/TruncatedLennardJones.h new file mode 100644 index 00000000..dd878d20 --- /dev/null +++ b/src/forces/TruncatedLennardJones.h @@ -0,0 +1,29 @@ +// +// Created by jkr on 12/25/24. +// + +#ifndef TRUNCATEDLENNARDJONES_H +#define TRUNCATEDLENNARDJONES_H +#include "InteractiveForce.h" + +/** + * @brief Repulsive Lennard Jones force that is truncated at 2^(1/6) * sigma + */ +class TruncatedLennardJones final : public InteractiveForce { + public: + /** + * @brief Default construtor for truncated Lennard Jones force + */ + explicit TruncatedLennardJones() = default; + + /** + * @brief calculates the repulsive part of the lennard jones force between two + * particles if they are close enough, 0 else. + * @param p1 source particle + * @param p2 target particle + * @return force from particle 1 to particle 2 + */ + dvec3 directionalForce(Particle& p1, Particle& p2) const override; +}; + +#endif // TRUNCATEDLENNARDJONES_H diff --git a/src/io/CLArgumentParser.cpp b/src/io/CLArgumentParser.cpp index 0ff8d62d..2846f8e8 100644 --- a/src/io/CLArgumentParser.cpp +++ b/src/io/CLArgumentParser.cpp @@ -8,36 +8,41 @@ #include #include +#include +#include #include #include "spdlog/fmt/bundled/chrono.h" #include "utils/SpdWrapper.h" -std::tuple CLArgumentParser::parse( - const int argc, char *argv[]) { - const option long_options[] = {{"help", no_argument, nullptr, 'h'}, - {"file", required_argument, nullptr, 'f'}, - {"step_size", required_argument, nullptr, 's'}, - {"loglevel", required_argument, nullptr, 'l'}, - {"checkpoint", no_argument, nullptr, 'c'}, - {nullptr, 0, nullptr, 0}}; +std::tuple> +CLArgumentParser::parse(const int argc, char *argv[]) { + SpdWrapper::get()->info("Parsing arguments"); + const option long_options[] = { + {"help", no_argument, nullptr, 'h'}, + {"file", required_argument, nullptr, 'f'}, + {"step_size", required_argument, nullptr, 's'}, + {"loglevel", required_argument, nullptr, 'l'}, + {"checkpoint", required_argument, nullptr, 'c'}, + {nullptr, 0, nullptr, 0}}; int opt; int option_index = 0; std::filesystem::path input_file{}; double step_size = 0.5; - bool write_checkpoint = false; - - while ((opt = getopt_long(argc, argv, "hf:s:l:c", long_options, + std::optional checkpoint_file{}; + while ((opt = getopt_long(argc, argv, "hf:s:l:c:", long_options, &option_index)) != -1) { + SpdWrapper::get()->info("Parsing options"); try { - if ((opt == 'f' || opt == 't' || opt == 'd' || opt == 's') && + if ((opt == 'f' || opt == 's' || opt == 'l' || opt == 'c') && optarg == nullptr) { throw std::invalid_argument("invalid argument for option -" + std::string(1, static_cast(opt))); } - + SpdWrapper::get()->info("Parsing -{} opt, {} optind", + static_cast(opt), option_index); switch (opt) { case 'h': printUsage("Display Help page, no execution", argv[0]); @@ -55,7 +60,7 @@ std::tuple CLArgumentParser::parse( } break; case 'c': - write_checkpoint = true; + checkpoint_file = optarg; break; default: throw std::invalid_argument("Unsupported option: -" + @@ -66,6 +71,7 @@ std::tuple CLArgumentParser::parse( exit(EXIT_FAILURE); } } + SpdWrapper::get()->info("Parsing"); try { validateInputFile(input_file); @@ -73,17 +79,26 @@ std::tuple CLArgumentParser::parse( printUsage(e.what(), argv[0]); exit(EXIT_FAILURE); } - return {input_file, step_size, write_checkpoint}; + + SpdWrapper::get()->info("Resetting getopt vars ... "); + + optind = 0; // Reset position + optarg = nullptr; // Reset argument pointer + optopt = 0; // Reset last option character + + return {input_file, step_size, checkpoint_file}; } void CLArgumentParser::validateInputFile( const std::filesystem::path &file_path) { + SpdWrapper::get()->warn("Validating input file: {}", file_path.string()); + if (!exists(file_path) || is_directory(file_path)) { printUsage("File does not exist", file_path); throw std::invalid_argument("Input file '" + std::string(file_path) + "' does not exist or is a directory"); } - + SpdWrapper::get()->info("Check if empty"); if (std::ifstream iss(file_path); iss.peek() == std::ifstream::traits_type::eof()) { throw std::invalid_argument("Input file '" + std::string(file_path) + @@ -91,10 +106,10 @@ void CLArgumentParser::validateInputFile( } } -void CLArgumentParser::printUsage(const std::string &additionalNote, - const std::string &programName) { +void CLArgumentParser::printUsage(const std::string &additional_note, + const std::string &program_name) { SpdWrapper::get()->set_level(spdlog::level::err); - SpdWrapper::get()->error(additionalNote); + SpdWrapper::get()->error(additional_note); SpdWrapper::get()->error( "Usage: {} [options]\n" "Options:\n" @@ -113,5 +128,5 @@ void CLArgumentParser::printUsage(const std::string &additionalNote, "Example:\n" " {} -f .xml --loglevel info --step_size " "0.01\n", - programName, programName); + program_name, program_name); } diff --git a/src/io/CLArgumentParser.h b/src/io/CLArgumentParser.h index 37a0fe3d..946f601a 100644 --- a/src/io/CLArgumentParser.h +++ b/src/io/CLArgumentParser.h @@ -7,6 +7,7 @@ #include #include +#include #include #include "defs/Simulation.h" @@ -23,18 +24,20 @@ class CLArgumentParser { * @note prints usage if failed * @param argc Directly from main * @param argv Directly from main - * @returns path to the input file + * @returns tuple of (path to the input file, the step size, and if given, the + * path to the checkpoint) */ - static std::tuple parse(int argc, - char *argv[]); + static std::tuple> + parse(int argc, char *argv[]); /** * @brief print usage - * @param additionalNote additional info to customize printout - * @param programName name of the program (argv[0]) + * @param additional_note additional info to customize printout + * @param program_name name of the program (argv[0]) */ - static void printUsage(const std::string &additionalNote, - const std::string &programName); + static void printUsage(const std::string &additional_note, + const std::string &program_name); /** * @brief Validates the existence and validity of an input file. diff --git a/src/io/file/in/xml/XmlReader.cpp b/src/io/file/in/xml/XmlReader.cpp index 60bd2aba..22bf070e 100644 --- a/src/io/file/in/xml/XmlReader.cpp +++ b/src/io/file/in/xml/XmlReader.cpp @@ -5,9 +5,11 @@ #include "XmlReader.h" #include +#include #include "debug/debug_print.h" #include "defs/Generators/CuboidGenerator.h" +#include "defs/Generators/MembraneGenerator.h" #include "defs/Generators/SpheroidGenerator.h" #include "defs/types.h" #include "forces/Gravity.h" @@ -20,11 +22,12 @@ void XmlReader::read(std::vector& particles, const std::string& filepath, Arguments& simulation_parameters) { const std::filesystem::path path(filepath); - validate_path(path, ".xml", "Simulation input"); + validatePath(path, ".xml", "Simulation input"); try { const std::unique_ptr<::simulation> config = simulation_(filepath); - SpdWrapper::get()->info("Reading XML file {}", filepath); + INFO_FMT("Reading XML file {}", filepath); auto& metadata = config->metadata(); + INFO("Reading containers ...") if (auto& container = metadata.container(); container.directSum().present()) { DirectSumConfig direct_sum_config; @@ -55,6 +58,7 @@ void XmlReader::read(std::vector& particles, SpdWrapper::get()->warn( "No container provided, using default LinkedCells"); } + INFO("Reading interactive forces ...") if (auto& force = metadata.force(); force.LennardJones().present()) { simulation_parameters.interactive_force_types.emplace_back( LennardJonesConfig{}); @@ -63,41 +67,117 @@ void XmlReader::read(std::vector& particles, simulation_parameters.interactive_force_types.emplace_back( GravityConfig{}); DEBUG_PRINT("Using Gravity"); + } else if (force.TruncatedLennardJonesForce().present()) { + simulation_parameters.interactive_force_types.emplace_back( + TruncatedLennardJonesConfig{}); + DEBUG_PRINT("Using TruncatedLennardJonesForce"); } else { SpdWrapper::get()->warn("No force provided, using default LennardJones"); } + INFO("Reading singular foces ...") if (auto& singular_force = metadata.force(); singular_force.SingularGravity().present()) { + SpdWrapper::get()->info("Adding singular gravity on axis {}", + singular_force.SingularGravity()->axis()); + + simulation_parameters.singular_force_types.emplace_back( + SingularGravityConfig{singular_force.SingularGravity()->g(), + singular_force.SingularGravity()->axis()}); + } + if (auto& singular_force = metadata.force(); + singular_force.HarmonicForce().present()) { simulation_parameters.singular_force_types.emplace_back( - SingularGravityConfig{singular_force.SingularGravity()->g().get()}); + HarmonicForceConfig{singular_force.HarmonicForce()->r_0(), + singular_force.HarmonicForce()->k()}); + DEBUG_PRINT("Using HarmonicForce"); } + if (auto& index_force = metadata.force(); + index_force.IndexForce().present()) { + SpdWrapper::get()->info("Building index force"); + std::vector indeces{}; + for (auto& i : index_force.IndexForce()->index()) { + indeces.push_back(unwrapVec(i, "index")); + } + SpdWrapper::get()->info("indeces[0] {}", indeces[0][0]); + IndexForceConfig index_force_config{ + indeces, + {}, + index_force.IndexForce()->time(), + unwrapVec( + index_force.IndexForce()->force_values(), "force_values"), + }; + simulation_parameters.index_force_configs.push_back(index_force_config); + } + INFO("Checking if checkpoint is present ...") if (metadata.checkpoint().present()) { - loadCheckpoint(metadata.checkpoint().get(), particles); + INFO("Membrane checkpoint present") + auto name = static_cast(metadata.checkpoint()->name()); + if (bool is_membrane = metadata.checkpoint()->is_membrane()) { + ivec3 dimensions = unwrapVec( + metadata.checkpoint()->domain(), "domain of membrane checkpoint"); + loadCheckpointMembrane(name, dimensions, particles); + } else { + loadCheckpoint(name, particles); + } + } + + StatisticsConfig statistics_config; + if (metadata.statistics().present()) { + auto statistics = metadata.statistics().get(); + statistics_config = { + .calc_stats = true, + .x_bins = statistics.x_bins(), + .y_bins = statistics.y_bins(), + .output_interval = statistics.output_interval(), + .velocity_output_location = "velocity.csv", + .density_output_location = "density.csv", + }; + } else { + statistics_config = { + .calc_stats = false, + .velocity_output_location = "velocity.csv", + .density_output_location = "density.csv", + }; + } + if (metadata.use_c18_strategy().present()) { + if (metadata.use_c18_strategy().get()) { + INFO("Using c18 strategy") + simulation_parameters.strategy = ParallelStrategy::STRATEGY_2; + } else { + INFO("Using force buffers reader") + simulation_parameters.strategy = ParallelStrategy::STRATEGY_1; + } + } else { + simulation_parameters.strategy = ParallelStrategy::STRATEGY_3; } + simulation_parameters.statistics_config = statistics_config; + + INFO("Checking for thermostat ...") if (config->thermostat().present()) { auto thermostat = config->thermostat(); ThermostatConfig thermostat_config = { - .T_init = thermostat->T_init(), - .T_target = + .t_init = thermostat->T_init(), + .t_target = thermostat->T_init(), // we initialize both this and deltaT to // its defaults first and then check if the // actual values are present - .deltaT = + .delta_t = std::numeric_limits::max(), // Default to infinity, dont // use infinity() limit // because of -ffast-math .n_thermostat = thermostat->n_thermostat(), + .use_thermal_motion = + static_cast(thermostat->use_thermal_motion()), + .two_d = static_cast(config->metadata().twoD()), }; if (thermostat->deltaT().present()) { - thermostat_config.deltaT = thermostat->deltaT().get(); + thermostat_config.delta_t = thermostat->deltaT().get(); } if (thermostat->T_target().present()) { - thermostat_config.T_target = thermostat->T_target().get(); + thermostat_config.t_target = thermostat->T_target().get(); } - SpdWrapper::get()->info("Checkpoint thermostat deltaT {}", - thermostat_config.deltaT); simulation_parameters.thermostat_config = thermostat_config; simulation_parameters.use_thermostat = true; } else { @@ -105,10 +185,13 @@ void XmlReader::read(std::vector& particles, } simulation_parameters.delta_t = metadata.delta_t(); simulation_parameters.t_end = metadata.t_end(); - const auto& twoD = metadata.twoD(); + const auto& twoD = metadata.twoD(); + INFO_FMT("Simulation has dimension {}", twoD ? 2 : 3) + INFO("Reading in particle shape configurations ...") if (config->cuboids() != nullptr) { for (const auto& cubes : config->cuboids()->cuboid()) { + SpdWrapper::get()->info("Generating cuboid"); const auto& _corner = cubes.corner(); const auto& _dimensions = cubes.dimensions(); const auto& _velocity = cubes.velocity(); @@ -120,20 +203,57 @@ void XmlReader::read(std::vector& particles, double mv; if (config->thermostat().present() && velocity == dvec3{0.0, 0.0, 0.0}) { - mv = std::sqrt(simulation_parameters.thermostat_config.T_init / + mv = std::sqrt(simulation_parameters.thermostat_config.t_init / cubes.mass()); } else { mv = cubes.mv(); } + // These info print themselves CuboidGenerator cg(corner, dimensions, cubes.h(), cubes.mass(), velocity, mv, cubes.epsilon(), cubes.sigma(), cubes.type(), twoD); - cg.generate(particles); } } + if (config->membranes() != nullptr) { + std::cout << "membrane_config found" << std::endl; + for (const auto& membranes : config->membranes()->membrane()) { + SpdWrapper::get()->info("Generating membranes"); + const auto& _corner = membranes.corner(); + const auto& _dimensions = membranes.dimensions(); + const auto& _velocity = membranes.velocity(); + dvec3 corner = unwrapVec(_corner, "corner"); + ivec3 dimensions = + unwrapVec(_dimensions, "dimensions"); + dvec3 velocity = + unwrapVec(_velocity, "velocity"); + double mv; + if (config->thermostat().present() && + velocity == dvec3{0.0, 0.0, 0.0}) { + mv = std::sqrt(simulation_parameters.thermostat_config.t_init / + membranes.mass()); + } else { + mv = membranes.mv(); + } + MembraneGenerator mg( + corner, dimensions, membranes.h(), membranes.mass(), velocity, mv, + membranes.epsilon(), membranes.sigma(), membranes.type(), twoD, + simulation_parameters.index_force_configs[0].indeces); + mg.generate(particles); + std::vector ids = mg.getIndices(); + simulation_parameters.index_force_configs[0].ids = ids; + if (std::holds_alternative( + simulation_parameters.container_data)) { + LinkedCellsConfig linked_cells_config_tmp = + std::get(simulation_parameters.container_data); + linked_cells_config_tmp.is_membrane = true; + simulation_parameters.container_data = linked_cells_config_tmp; + } + } + } + if (config->spheroids() != nullptr) { for (const auto& spheres : config->spheroids()->spheroid()) { const auto& _origin = spheres.origin(); @@ -143,11 +263,12 @@ void XmlReader::read(std::vector& particles, double mv; if (config->thermostat().present() && velocity == dvec3{0.0, 0.0, 0.0}) { - mv = std::sqrt(simulation_parameters.thermostat_config.T_init / + mv = std::sqrt(simulation_parameters.thermostat_config.t_init / spheres.mass()); } else { mv = spheres.mv(); } + SpheroidGenerator sg(origin, spheres.radius(), spheres.h(), spheres.mass(), velocity, spheres.epsilon(), spheres.sigma(), spheres.type(), mv, twoD); @@ -155,7 +276,6 @@ void XmlReader::read(std::vector& particles, sg.generate(particles); } } - } catch (const std::exception& e) { SpdWrapper::get()->error("Error reading XML file: {}", e.what()); exit(EXIT_FAILURE); @@ -164,13 +284,12 @@ void XmlReader::read(std::vector& particles, void XmlReader::loadCheckpoint(const std::string& _filepath, std::vector& particles) { - SpdWrapper::get()->info("Looking and for checkpoint file"); const std::filesystem::path filepath(_filepath); - validate_path(filepath, ".xml", "checkpoint"); + validatePath(filepath, ".xml", "checkpoint"); try { DEBUG_PRINT("Found checkpoint file"); const std::unique_ptr<::CheckpointType> checkpoint = Checkpoint(filepath); - SpdWrapper::get()->info("Reading checkpoint particles"); + INFO("Reading checkpoint particles"); std::vector temp_particles; for (const auto& p : checkpoint->Particles().Particle()) { auto position = @@ -187,8 +306,7 @@ void XmlReader::loadCheckpoint(const std::string& _filepath, temp_particles.emplace_back(position, velocity, force, old_force, mass, type, epsilon, sigma); } - SpdWrapper::get()->info("Read {} particles from checkpoint", - temp_particles.size()); + INFO_FMT("Read {} particles from checkpoint", temp_particles.size()); particles.reserve(particles.size() + temp_particles.size()); particles.insert(particles.end(), temp_particles.begin(), temp_particles.end()); @@ -197,6 +315,102 @@ void XmlReader::loadCheckpoint(const std::string& _filepath, } } +void XmlReader::loadCheckpointMembrane(const std::string& _filepath, + const ivec3& dimensions, + std::vector& particles) { + INFO("Loading membrane checkpoint") + const std::filesystem::path filepath(_filepath); + validatePath(filepath, ".xml", "checkpoint"); + try { + DEBUG_PRINT("Found checkpoint file"); + const std::unique_ptr<::CheckpointType> checkpoint = Checkpoint(filepath); + INFO("Reading checkpoint particles"); + std::vector temp_particles; + for (const auto& p : checkpoint->Particles().Particle()) { + auto position = + unwrapVec(p.Position(), "position"); + auto velocity = + unwrapVec(p.Velocity(), "position"); + auto force = unwrapVec(p.Force(), "force"); + auto old_force = + unwrapVec(p.OldForce(), "old_force"); + double mass = p.mass(); + double epsilon = p.epsilon(); + double sigma = p.sigma(); + int type = p.type(); + temp_particles.emplace_back(position, velocity, force, old_force, mass, + type, epsilon, sigma); + } + INFO_FMT("Read {} particles from checkpoint", temp_particles.size()); + std::size_t previous_size = particles.size(); + particles.reserve(previous_size + temp_particles.size()); + particles.insert(particles.end(), temp_particles.begin(), + temp_particles.end()); + + for (int i = 0; i < dimensions[0]; i++) { + for (int j = 0; j < dimensions[1]; j++) { + for (int k = 0; k < dimensions[2]; k++) { + const std::size_t current_index = + i * dimensions[1] * dimensions[2] + j * dimensions[2] + k; + + // Iterate over all neighbors including diagonals + for (int di = -1; di <= 1; di++) { + for (int dj = -1; dj <= 1; dj++) { + for (int dk = -1; dk <= 1; dk++) { + if (di == 0 && dj == 0 && dk == 0) { + continue; + } + + const long ni = i + di; + const long nj = j + dj; + const long nk = k + dk; + + if (ni >= 0 && ni < dimensions[0] && nj >= 0 && + nj < dimensions[1] && nk >= 0 && nk < dimensions[2]) { + const long neighbor_index = + ni * dimensions[1] * dimensions[2] + nj * dimensions[2] + + nk; + const bool is_diagonal = + (di != 0) + (dj != 0) + (dk != 0) > 1; + Particle* neighbor_particle = + &particles[previous_size + neighbor_index]; + particles[previous_size + current_index].pushBackNeighbour( + is_diagonal, reinterpret_cast(neighbor_particle)); + } + } + } + } + } + } + } + DEBUG_PRINT("particles: " + std::to_string(particles.size())); + + } catch (const std::exception& e) { + SpdWrapper::get()->error("Error reading checkpoint file: {}", e.what()); + } +} + +void XmlReader::validateBoundaries( + const LinkedCellsConfig::BoundaryConfig& boundary) { + if (boundary.x_high != boundary.x_low && + (boundary.x_high == LinkedCellsConfig::Periodic || + boundary.x_low == LinkedCellsConfig::Periodic)) { + throw std::runtime_error("x dimension has incompatible boundaries"); + } + + if (boundary.y_high != boundary.y_low && + (boundary.y_high == LinkedCellsConfig::Periodic || + boundary.y_low == LinkedCellsConfig::Periodic)) { + throw std::runtime_error("y dimension has incompatible boundaries"); + } + + if (boundary.z_high != boundary.z_low && + (boundary.z_high == LinkedCellsConfig::Periodic || + boundary.z_low == LinkedCellsConfig::Periodic)) { + throw std::runtime_error("z dimension has incompatible boundaries"); + } +} + using LBoundaryType = LinkedCellsConfig::BoundaryType; // BoundaryType is double used by the // xsd config files so be careful diff --git a/src/io/file/in/xml/XmlReader.h b/src/io/file/in/xml/XmlReader.h index 3edbe444..d61cf8be 100644 --- a/src/io/file/in/xml/XmlReader.h +++ b/src/io/file/in/xml/XmlReader.h @@ -33,39 +33,59 @@ class XmlReader { * @param boundary the boundary configuration */ static inline void validateBoundaries( - const LinkedCellsConfig::BoundaryConfig& boundary) { - if (boundary.x_high != boundary.x_low && - (boundary.x_high == LinkedCellsConfig::Periodic || - boundary.x_low == LinkedCellsConfig::Periodic)) { - throw std::runtime_error("x dimension has incompatible boundaries"); - } - - if (boundary.y_high != boundary.y_low && - (boundary.y_high == LinkedCellsConfig::Periodic || - boundary.y_low == LinkedCellsConfig::Periodic)) { - throw std::runtime_error("y dimension has incompatible boundaries"); - } - - if (boundary.z_high != boundary.z_low && - (boundary.z_high == LinkedCellsConfig::Periodic || - boundary.z_low == LinkedCellsConfig::Periodic)) { - throw std::runtime_error("z dimension has incompatible boundaries"); - } - } + const LinkedCellsConfig::BoundaryConfig& boundary); + /** + * @brief Reads a xml checkpoint file as specified in + * MolSim/src/io/file/out/checkpoint-schema.xsd + * @param filepath path pointing to checkpoint + * @param particles particles to which the checkpoint is read + */ static void loadCheckpoint(const std::string& filepath, std::vector& particles); + + /** + * @brief Reads a checkpoint file + * @param filepath path pointing to checkpoint containing membrane + * @param dimensions the dimensions of the original membrane cuboid / membrane + * generator + * @param particles particles to which the checkpoint is read + */ + static void loadCheckpointMembrane(const std::string& filepath, + const ivec3& dimensions, + std::vector& particles); }; +/** + * @brief validates that the number of bins are correct and not smaller than 1 + * and the output time is larger than 0 + * @param stats Statistics config to validate + */ +inline void validateStatisticsInput(const StatisticsConfig& stats) { + if (stats.x_bins < 1 || stats.y_bins < 1) { + SpdWrapper::get()->error( + "Number of x-bins ({}) or y-bins ({}) is too small", stats.x_bins, + stats.y_bins); + throw std::runtime_error("Number of x-bins or y-bins is too small"); + } + + if (stats.output_interval < 1) { + SpdWrapper::get()->error( + "Output interval for the statistics is too small ({})", + stats.output_interval); + throw std::runtime_error("Output interval for the statistics is too small"); + } +} + /** * @brief ensures the input files are valid (not empty, extension, path) * @param path the path pointing to the file * @param extension the desired file extension * @param type the type of file you are checking (Input, checkpoint, etc) */ -inline void validate_path(const std::filesystem::path& path, - const std::string& extension, - const std::string& type) { +inline void validatePath(const std::filesystem::path& path, + const std::string& extension, + const std::string& type) { if (!exists(path)) { throw std::runtime_error(type + " file not found: " + path.string()); } @@ -84,8 +104,8 @@ inline void validate_path(const std::filesystem::path& path, * specification * @return the enum type for arguments */ -template -LinkedCellsConfig::BoundaryType toBoundaryType(const BT& boundary_type); +template +LinkedCellsConfig::BoundaryType toBoundaryType(const Bt& boundary_type); /** * @brief translates a vector from the xml parser to a valid "standard" c++ type diff --git a/src/io/file/in/xml/input.cxx b/src/io/file/in/xml/input.cxx index ffaea0cc..4d15bf4e 100644 --- a/src/io/file/in/xml/input.cxx +++ b/src/io/file/in/xml/input.cxx @@ -59,6 +59,23 @@ void MetadataType::container(::std::auto_ptr x) { this->container_.set(x); } +const MetadataType::use_c18_strategy_optional& MetadataType::use_c18_strategy() + const { + return this->use_c18_strategy_; +} + +MetadataType::use_c18_strategy_optional& MetadataType::use_c18_strategy() { + return this->use_c18_strategy_; +} + +void MetadataType::use_c18_strategy(const use_c18_strategy_type& x) { + this->use_c18_strategy_.set(x); +} + +void MetadataType::use_c18_strategy(const use_c18_strategy_optional& x) { + this->use_c18_strategy_ = x; +} + const MetadataType::force_type& MetadataType::force() const { return this->force_.get(); } @@ -115,6 +132,112 @@ void MetadataType::checkpoint(::std::auto_ptr x) { this->checkpoint_.set(x); } +const MetadataType::statistics_optional& MetadataType::statistics() const { + return this->statistics_; +} + +MetadataType::statistics_optional& MetadataType::statistics() { + return this->statistics_; +} + +void MetadataType::statistics(const statistics_type& x) { + this->statistics_.set(x); +} + +void MetadataType::statistics(const statistics_optional& x) { + this->statistics_ = x; +} + +void MetadataType::statistics(::std::auto_ptr x) { + this->statistics_.set(x); +} + +// StrategyType +// + +// CheckpointWrapperType +// + +const CheckpointWrapperType::name_type& CheckpointWrapperType::name() const { + return this->name_.get(); +} + +CheckpointWrapperType::name_type& CheckpointWrapperType::name() { + return this->name_.get(); +} + +void CheckpointWrapperType::name(const name_type& x) { this->name_.set(x); } + +void CheckpointWrapperType::name(::std::auto_ptr x) { + this->name_.set(x); +} + +const CheckpointWrapperType::is_membrane_type& +CheckpointWrapperType::is_membrane() const { + return this->is_membrane_.get(); +} + +CheckpointWrapperType::is_membrane_type& CheckpointWrapperType::is_membrane() { + return this->is_membrane_.get(); +} + +void CheckpointWrapperType::is_membrane(const is_membrane_type& x) { + this->is_membrane_.set(x); +} + +const CheckpointWrapperType::domain_type& CheckpointWrapperType::domain() + const { + return this->domain_.get(); +} + +CheckpointWrapperType::domain_type& CheckpointWrapperType::domain() { + return this->domain_.get(); +} + +void CheckpointWrapperType::domain(const domain_type& x) { + this->domain_.set(x); +} + +void CheckpointWrapperType::domain(::std::auto_ptr x) { + this->domain_.set(x); +} + +// StatisticsType +// + +const StatisticsType::x_bins_type& StatisticsType::x_bins() const { + return this->x_bins_.get(); +} + +StatisticsType::x_bins_type& StatisticsType::x_bins() { + return this->x_bins_.get(); +} + +void StatisticsType::x_bins(const x_bins_type& x) { this->x_bins_.set(x); } + +const StatisticsType::y_bins_type& StatisticsType::y_bins() const { + return this->y_bins_.get(); +} + +StatisticsType::y_bins_type& StatisticsType::y_bins() { + return this->y_bins_.get(); +} + +void StatisticsType::y_bins(const y_bins_type& x) { this->y_bins_.set(x); } + +const StatisticsType::output_interval_type& StatisticsType::output_interval() + const { + return this->output_interval_.get(); +} + +StatisticsType::output_interval_type& StatisticsType::output_interval() { + return this->output_interval_.get(); +} + +void StatisticsType::output_interval(const output_interval_type& x) { + this->output_interval_.set(x); +} + // cuboidType // @@ -204,6 +327,101 @@ cuboidType::mv_type& cuboidType::mv() { return this->mv_.get(); } void cuboidType::mv(const mv_type& x) { this->mv_.set(x); } +// membraneType +// + +const membraneType::velocity_type& membraneType::velocity() const { + return this->velocity_.get(); +} + +membraneType::velocity_type& membraneType::velocity() { + return this->velocity_.get(); +} + +void membraneType::velocity(const velocity_type& x) { this->velocity_.set(x); } + +void membraneType::velocity(::std::auto_ptr x) { + this->velocity_.set(x); +} + +const membraneType::corner_type& membraneType::corner() const { + return this->corner_.get(); +} + +membraneType::corner_type& membraneType::corner() { + return this->corner_.get(); +} + +void membraneType::corner(const corner_type& x) { this->corner_.set(x); } + +void membraneType::corner(::std::auto_ptr x) { + this->corner_.set(x); +} + +const membraneType::dimensions_type& membraneType::dimensions() const { + return this->dimensions_.get(); +} + +membraneType::dimensions_type& membraneType::dimensions() { + return this->dimensions_.get(); +} + +void membraneType::dimensions(const dimensions_type& x) { + this->dimensions_.set(x); +} + +void membraneType::dimensions(::std::auto_ptr x) { + this->dimensions_.set(x); +} + +const membraneType::type_type& membraneType::type() const { + return this->type_.get(); +} + +membraneType::type_type& membraneType::type() { return this->type_.get(); } + +void membraneType::type(const type_type& x) { this->type_.set(x); } + +const membraneType::h_type& membraneType::h() const { return this->h_.get(); } + +membraneType::h_type& membraneType::h() { return this->h_.get(); } + +void membraneType::h(const h_type& x) { this->h_.set(x); } + +const membraneType::mass_type& membraneType::mass() const { + return this->mass_.get(); +} + +membraneType::mass_type& membraneType::mass() { return this->mass_.get(); } + +void membraneType::mass(const mass_type& x) { this->mass_.set(x); } + +const membraneType::epsilon_type& membraneType::epsilon() const { + return this->epsilon_.get(); +} + +membraneType::epsilon_type& membraneType::epsilon() { + return this->epsilon_.get(); +} + +void membraneType::epsilon(const epsilon_type& x) { this->epsilon_.set(x); } + +const membraneType::sigma_type& membraneType::sigma() const { + return this->sigma_.get(); +} + +membraneType::sigma_type& membraneType::sigma() { return this->sigma_.get(); } + +void membraneType::sigma(const sigma_type& x) { this->sigma_.set(x); } + +const membraneType::mv_type& membraneType::mv() const { + return this->mv_.get(); +} + +membraneType::mv_type& membraneType::mv() { return this->mv_.get(); } + +void membraneType::mv(const mv_type& x) { this->mv_.set(x); } + // spheroidType // @@ -625,24 +843,156 @@ void ForceType::SingularGravity(::std::auto_ptr x) { this->SingularGravity_.set(x); } +const ForceType::HarmonicForce_optional& ForceType::HarmonicForce() const { + return this->HarmonicForce_; +} + +ForceType::HarmonicForce_optional& ForceType::HarmonicForce() { + return this->HarmonicForce_; +} + +void ForceType::HarmonicForce(const HarmonicForce_type& x) { + this->HarmonicForce_.set(x); +} + +void ForceType::HarmonicForce(const HarmonicForce_optional& x) { + this->HarmonicForce_ = x; +} + +void ForceType::HarmonicForce(::std::auto_ptr x) { + this->HarmonicForce_.set(x); +} + +const ForceType::IndexForce_optional& ForceType::IndexForce() const { + return this->IndexForce_; +} + +ForceType::IndexForce_optional& ForceType::IndexForce() { + return this->IndexForce_; +} + +void ForceType::IndexForce(const IndexForce_type& x) { + this->IndexForce_.set(x); +} + +void ForceType::IndexForce(const IndexForce_optional& x) { + this->IndexForce_ = x; +} + +void ForceType::IndexForce(::std::auto_ptr x) { + this->IndexForce_.set(x); +} + +const ForceType::TruncatedLennardJonesForce_optional& +ForceType::TruncatedLennardJonesForce() const { + return this->TruncatedLennardJonesForce_; +} + +ForceType::TruncatedLennardJonesForce_optional& +ForceType::TruncatedLennardJonesForce() { + return this->TruncatedLennardJonesForce_; +} + +void ForceType::TruncatedLennardJonesForce( + const TruncatedLennardJonesForce_type& x) { + this->TruncatedLennardJonesForce_.set(x); +} + +void ForceType::TruncatedLennardJonesForce( + const TruncatedLennardJonesForce_optional& x) { + this->TruncatedLennardJonesForce_ = x; +} + +void ForceType::TruncatedLennardJonesForce( + ::std::auto_ptr x) { + this->TruncatedLennardJonesForce_.set(x); +} + // GravityType // // LennardJonesForce // +// TruncatedLennardJonesForceType +// + // SingularGravityType // -const SingularGravityType::g_optional& SingularGravityType::g() const { - return this->g_; +const SingularGravityType::g_type& SingularGravityType::g() const { + return this->g_.get(); } -SingularGravityType::g_optional& SingularGravityType::g() { return this->g_; } +SingularGravityType::g_type& SingularGravityType::g() { return this->g_.get(); } void SingularGravityType::g(const g_type& x) { this->g_.set(x); } -void SingularGravityType::g(const g_optional& x) { this->g_ = x; } +const SingularGravityType::axis_type& SingularGravityType::axis() const { + return this->axis_.get(); +} + +SingularGravityType::axis_type& SingularGravityType::axis() { + return this->axis_.get(); +} + +void SingularGravityType::axis(const axis_type& x) { this->axis_.set(x); } + +// HarmonicForceType +// + +const HarmonicForceType::r_0_type& HarmonicForceType::r_0() const { + return this->r_0_.get(); +} + +HarmonicForceType::r_0_type& HarmonicForceType::r_0() { + return this->r_0_.get(); +} + +void HarmonicForceType::r_0(const r_0_type& x) { this->r_0_.set(x); } + +const HarmonicForceType::k_type& HarmonicForceType::k() const { + return this->k_.get(); +} + +HarmonicForceType::k_type& HarmonicForceType::k() { return this->k_.get(); } + +void HarmonicForceType::k(const k_type& x) { this->k_.set(x); } + +// IndexForceType +// + +const IndexForceType::index_sequence& IndexForceType::index() const { + return this->index_; +} + +IndexForceType::index_sequence& IndexForceType::index() { return this->index_; } + +void IndexForceType::index(const index_sequence& s) { this->index_ = s; } + +const IndexForceType::time_type& IndexForceType::time() const { + return this->time_.get(); +} + +IndexForceType::time_type& IndexForceType::time() { return this->time_.get(); } + +void IndexForceType::time(const time_type& x) { this->time_.set(x); } + +const IndexForceType::force_values_type& IndexForceType::force_values() const { + return this->force_values_.get(); +} + +IndexForceType::force_values_type& IndexForceType::force_values() { + return this->force_values_.get(); +} + +void IndexForceType::force_values(const force_values_type& x) { + this->force_values_.set(x); +} + +void IndexForceType::force_values(::std::auto_ptr x) { + this->force_values_.set(x); +} // ThermostatType // @@ -697,6 +1047,24 @@ void ThermostatType::deltaT(const deltaT_type& x) { this->deltaT_.set(x); } void ThermostatType::deltaT(const deltaT_optional& x) { this->deltaT_ = x; } +const ThermostatType::use_thermal_motion_optional& +ThermostatType::use_thermal_motion() const { + return this->use_thermal_motion_; +} + +ThermostatType::use_thermal_motion_optional& +ThermostatType::use_thermal_motion() { + return this->use_thermal_motion_; +} + +void ThermostatType::use_thermal_motion(const use_thermal_motion_type& x) { + this->use_thermal_motion_.set(x); +} + +void ThermostatType::use_thermal_motion(const use_thermal_motion_optional& x) { + this->use_thermal_motion_ = x; +} + // simulation // @@ -746,6 +1114,24 @@ void simulation::spheroids(::std::auto_ptr x) { this->spheroids_.set(x); } +const simulation::membranes_optional& simulation::membranes() const { + return this->membranes_; +} + +simulation::membranes_optional& simulation::membranes() { + return this->membranes_; +} + +void simulation::membranes(const membranes_type& x) { this->membranes_.set(x); } + +void simulation::membranes(const membranes_optional& x) { + this->membranes_ = x; +} + +void simulation::membranes(::std::auto_ptr x) { + this->membranes_.set(x); +} + const simulation::thermostat_optional& simulation::thermostat() const { return this->thermostat_; } @@ -788,6 +1174,17 @@ spheroids::spheroid_sequence& spheroids::spheroid() { return this->spheroid_; } void spheroids::spheroid(const spheroid_sequence& s) { this->spheroid_ = s; } +// membranes +// + +const membranes::membrane_sequence& membranes::membrane() const { + return this->membrane_; +} + +membranes::membrane_sequence& membranes::membrane() { return this->membrane_; } + +void membranes::membrane(const membrane_sequence& s) { this->membrane_ = s; } + #include // MetadataType @@ -798,11 +1195,13 @@ MetadataType::MetadataType(const container_type& container, const t_end_type& t_end, const twoD_type& twoD) : ::xml_schema::type(), container_(container, this), + use_c18_strategy_(this), force_(force, this), delta_t_(delta_t, this), t_end_(t_end, this), twoD_(twoD, this), - checkpoint_(this) {} + checkpoint_(this), + statistics_(this) {} MetadataType::MetadataType(::std::auto_ptr container, ::std::auto_ptr force, @@ -810,31 +1209,37 @@ MetadataType::MetadataType(::std::auto_ptr container, const twoD_type& twoD) : ::xml_schema::type(), container_(container, this), + use_c18_strategy_(this), force_(force, this), delta_t_(delta_t, this), t_end_(t_end, this), twoD_(twoD, this), - checkpoint_(this) {} + checkpoint_(this), + statistics_(this) {} MetadataType::MetadataType(const MetadataType& x, ::xml_schema::flags f, ::xml_schema::container* c) : ::xml_schema::type(x, f, c), container_(x.container_, f, this), + use_c18_strategy_(x.use_c18_strategy_, f, this), force_(x.force_, f, this), delta_t_(x.delta_t_, f, this), t_end_(x.t_end_, f, this), twoD_(x.twoD_, f, this), - checkpoint_(x.checkpoint_, f, this) {} + checkpoint_(x.checkpoint_, f, this), + statistics_(x.statistics_, f, this) {} MetadataType::MetadataType(const ::xercesc::DOMElement& e, ::xml_schema::flags f, ::xml_schema::container* c) : ::xml_schema::type(e, f | ::xml_schema::flags::base, c), container_(this), + use_c18_strategy_(this), force_(this), delta_t_(this), t_end_(this), twoD_(this), - checkpoint_(this) { + checkpoint_(this), + statistics_(this) { if ((f & ::xml_schema::flags::base) == 0) { ::xsd::cxx::xml::dom::parser p(e, true, false, false); this->parse(p, f); @@ -859,6 +1264,16 @@ void MetadataType::parse(::xsd::cxx::xml::dom::parser& p, } } + // use_c18_strategy + // + if (n.name() == "use_c18_strategy" && n.namespace_().empty()) { + if (!this->use_c18_strategy_) { + this->use_c18_strategy_.set( + use_c18_strategy_traits::create(i, f, this)); + continue; + } + } + // force // if (n.name() == "force" && n.namespace_().empty()) { @@ -908,59 +1323,539 @@ void MetadataType::parse(::xsd::cxx::xml::dom::parser& p, } } + // statistics + // + if (n.name() == "statistics" && n.namespace_().empty()) { + ::std::auto_ptr r(statistics_traits::create(i, f, this)); + + if (!this->statistics_) { + this->statistics_.set(r); + continue; + } + } + break; } - if (!container_.present()) { - throw ::xsd::cxx::tree::expected_element("container", ""); + if (!container_.present()) { + throw ::xsd::cxx::tree::expected_element("container", ""); + } + + if (!force_.present()) { + throw ::xsd::cxx::tree::expected_element("force", ""); + } + + if (!delta_t_.present()) { + throw ::xsd::cxx::tree::expected_element("delta_t", ""); + } + + if (!t_end_.present()) { + throw ::xsd::cxx::tree::expected_element("t_end", ""); + } + + if (!twoD_.present()) { + throw ::xsd::cxx::tree::expected_element("twoD", ""); + } +} + +MetadataType* MetadataType::_clone(::xml_schema::flags f, + ::xml_schema::container* c) const { + return new class MetadataType(*this, f, c); +} + +MetadataType& MetadataType::operator=(const MetadataType& x) { + if (this != &x) { + static_cast< ::xml_schema::type&>(*this) = x; + this->container_ = x.container_; + this->use_c18_strategy_ = x.use_c18_strategy_; + this->force_ = x.force_; + this->delta_t_ = x.delta_t_; + this->t_end_ = x.t_end_; + this->twoD_ = x.twoD_; + this->checkpoint_ = x.checkpoint_; + this->statistics_ = x.statistics_; + } + + return *this; +} + +MetadataType::~MetadataType() {} + +// StrategyType +// + +StrategyType::StrategyType() : ::xml_schema::type() {} + +StrategyType::StrategyType(const StrategyType& x, ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(x, f, c) {} + +StrategyType::StrategyType(const ::xercesc::DOMElement& e, + ::xml_schema::flags f, ::xml_schema::container* c) + : ::xml_schema::type(e, f, c) {} + +StrategyType::StrategyType(const ::xercesc::DOMAttr& a, ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(a, f, c) {} + +StrategyType::StrategyType(const ::std::string& s, + const ::xercesc::DOMElement* e, + ::xml_schema::flags f, ::xml_schema::container* c) + : ::xml_schema::type(s, e, f, c) {} + +StrategyType* StrategyType::_clone(::xml_schema::flags f, + ::xml_schema::container* c) const { + return new class StrategyType(*this, f, c); +} + +StrategyType::~StrategyType() {} + +// CheckpointWrapperType +// + +CheckpointWrapperType::CheckpointWrapperType( + const name_type& name, const is_membrane_type& is_membrane, + const domain_type& domain) + : ::xml_schema::type(), + name_(name, this), + is_membrane_(is_membrane, this), + domain_(domain, this) {} + +CheckpointWrapperType::CheckpointWrapperType( + const name_type& name, const is_membrane_type& is_membrane, + ::std::auto_ptr domain) + : ::xml_schema::type(), + name_(name, this), + is_membrane_(is_membrane, this), + domain_(domain, this) {} + +CheckpointWrapperType::CheckpointWrapperType(const CheckpointWrapperType& x, + ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(x, f, c), + name_(x.name_, f, this), + is_membrane_(x.is_membrane_, f, this), + domain_(x.domain_, f, this) {} + +CheckpointWrapperType::CheckpointWrapperType(const ::xercesc::DOMElement& e, + ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(e, f | ::xml_schema::flags::base, c), + name_(this), + is_membrane_(this), + domain_(this) { + if ((f & ::xml_schema::flags::base) == 0) { + ::xsd::cxx::xml::dom::parser p(e, true, false, false); + this->parse(p, f); + } +} + +void CheckpointWrapperType::parse(::xsd::cxx::xml::dom::parser& p, + ::xml_schema::flags f) { + for (; p.more_content(); p.next_content(false)) { + const ::xercesc::DOMElement& i(p.cur_element()); + const ::xsd::cxx::xml::qualified_name n( + ::xsd::cxx::xml::dom::name(i)); + + // name + // + if (n.name() == "name" && n.namespace_().empty()) { + ::std::auto_ptr r(name_traits::create(i, f, this)); + + if (!name_.present()) { + this->name_.set(r); + continue; + } + } + + // is_membrane + // + if (n.name() == "is_membrane" && n.namespace_().empty()) { + if (!is_membrane_.present()) { + this->is_membrane_.set(is_membrane_traits::create(i, f, this)); + continue; + } + } + + // domain + // + if (n.name() == "domain" && n.namespace_().empty()) { + ::std::auto_ptr r(domain_traits::create(i, f, this)); + + if (!domain_.present()) { + this->domain_.set(r); + continue; + } + } + + break; + } + + if (!name_.present()) { + throw ::xsd::cxx::tree::expected_element("name", ""); + } + + if (!is_membrane_.present()) { + throw ::xsd::cxx::tree::expected_element("is_membrane", ""); + } + + if (!domain_.present()) { + throw ::xsd::cxx::tree::expected_element("domain", ""); + } +} + +CheckpointWrapperType* CheckpointWrapperType::_clone( + ::xml_schema::flags f, ::xml_schema::container* c) const { + return new class CheckpointWrapperType(*this, f, c); +} + +CheckpointWrapperType& CheckpointWrapperType::operator=( + const CheckpointWrapperType& x) { + if (this != &x) { + static_cast< ::xml_schema::type&>(*this) = x; + this->name_ = x.name_; + this->is_membrane_ = x.is_membrane_; + this->domain_ = x.domain_; + } + + return *this; +} + +CheckpointWrapperType::~CheckpointWrapperType() {} + +// StatisticsType +// + +StatisticsType::StatisticsType(const x_bins_type& x_bins, + const y_bins_type& y_bins, + const output_interval_type& output_interval) + : ::xml_schema::type(), + x_bins_(x_bins, this), + y_bins_(y_bins, this), + output_interval_(output_interval, this) {} + +StatisticsType::StatisticsType(const StatisticsType& x, ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(x, f, c), + x_bins_(x.x_bins_, f, this), + y_bins_(x.y_bins_, f, this), + output_interval_(x.output_interval_, f, this) {} + +StatisticsType::StatisticsType(const ::xercesc::DOMElement& e, + ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(e, f | ::xml_schema::flags::base, c), + x_bins_(this), + y_bins_(this), + output_interval_(this) { + if ((f & ::xml_schema::flags::base) == 0) { + ::xsd::cxx::xml::dom::parser p(e, true, false, false); + this->parse(p, f); + } +} + +void StatisticsType::parse(::xsd::cxx::xml::dom::parser& p, + ::xml_schema::flags f) { + for (; p.more_content(); p.next_content(false)) { + const ::xercesc::DOMElement& i(p.cur_element()); + const ::xsd::cxx::xml::qualified_name n( + ::xsd::cxx::xml::dom::name(i)); + + // x_bins + // + if (n.name() == "x_bins" && n.namespace_().empty()) { + if (!x_bins_.present()) { + this->x_bins_.set(x_bins_traits::create(i, f, this)); + continue; + } + } + + // y_bins + // + if (n.name() == "y_bins" && n.namespace_().empty()) { + if (!y_bins_.present()) { + this->y_bins_.set(y_bins_traits::create(i, f, this)); + continue; + } + } + + // output_interval + // + if (n.name() == "output_interval" && n.namespace_().empty()) { + if (!output_interval_.present()) { + this->output_interval_.set(output_interval_traits::create(i, f, this)); + continue; + } + } + + break; + } + + if (!x_bins_.present()) { + throw ::xsd::cxx::tree::expected_element("x_bins", ""); + } + + if (!y_bins_.present()) { + throw ::xsd::cxx::tree::expected_element("y_bins", ""); + } + + if (!output_interval_.present()) { + throw ::xsd::cxx::tree::expected_element("output_interval", ""); + } +} + +StatisticsType* StatisticsType::_clone(::xml_schema::flags f, + ::xml_schema::container* c) const { + return new class StatisticsType(*this, f, c); +} + +StatisticsType& StatisticsType::operator=(const StatisticsType& x) { + if (this != &x) { + static_cast< ::xml_schema::type&>(*this) = x; + this->x_bins_ = x.x_bins_; + this->y_bins_ = x.y_bins_; + this->output_interval_ = x.output_interval_; + } + + return *this; +} + +StatisticsType::~StatisticsType() {} + +// cuboidType +// + +cuboidType::cuboidType(const velocity_type& velocity, const corner_type& corner, + const dimensions_type& dimensions, const type_type& type, + const h_type& h, const mass_type& mass, + const epsilon_type& epsilon, const sigma_type& sigma, + const mv_type& mv) + : ::xml_schema::type(), + velocity_(velocity, this), + corner_(corner, this), + dimensions_(dimensions, this), + type_(type, this), + h_(h, this), + mass_(mass, this), + epsilon_(epsilon, this), + sigma_(sigma, this), + mv_(mv, this) {} + +cuboidType::cuboidType(::std::auto_ptr velocity, + ::std::auto_ptr corner, + ::std::auto_ptr dimensions, + const type_type& type, const h_type& h, + const mass_type& mass, const epsilon_type& epsilon, + const sigma_type& sigma, const mv_type& mv) + : ::xml_schema::type(), + velocity_(velocity, this), + corner_(corner, this), + dimensions_(dimensions, this), + type_(type, this), + h_(h, this), + mass_(mass, this), + epsilon_(epsilon, this), + sigma_(sigma, this), + mv_(mv, this) {} + +cuboidType::cuboidType(const cuboidType& x, ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(x, f, c), + velocity_(x.velocity_, f, this), + corner_(x.corner_, f, this), + dimensions_(x.dimensions_, f, this), + type_(x.type_, f, this), + h_(x.h_, f, this), + mass_(x.mass_, f, this), + epsilon_(x.epsilon_, f, this), + sigma_(x.sigma_, f, this), + mv_(x.mv_, f, this) {} + +cuboidType::cuboidType(const ::xercesc::DOMElement& e, ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(e, f | ::xml_schema::flags::base, c), + velocity_(this), + corner_(this), + dimensions_(this), + type_(this), + h_(this), + mass_(this), + epsilon_(this), + sigma_(this), + mv_(this) { + if ((f & ::xml_schema::flags::base) == 0) { + ::xsd::cxx::xml::dom::parser p(e, true, false, false); + this->parse(p, f); + } +} + +void cuboidType::parse(::xsd::cxx::xml::dom::parser& p, + ::xml_schema::flags f) { + for (; p.more_content(); p.next_content(false)) { + const ::xercesc::DOMElement& i(p.cur_element()); + const ::xsd::cxx::xml::qualified_name n( + ::xsd::cxx::xml::dom::name(i)); + + // velocity + // + if (n.name() == "velocity" && n.namespace_().empty()) { + ::std::auto_ptr r(velocity_traits::create(i, f, this)); + + if (!velocity_.present()) { + this->velocity_.set(r); + continue; + } + } + + // corner + // + if (n.name() == "corner" && n.namespace_().empty()) { + ::std::auto_ptr r(corner_traits::create(i, f, this)); + + if (!corner_.present()) { + this->corner_.set(r); + continue; + } + } + + // dimensions + // + if (n.name() == "dimensions" && n.namespace_().empty()) { + ::std::auto_ptr r(dimensions_traits::create(i, f, this)); + + if (!dimensions_.present()) { + this->dimensions_.set(r); + continue; + } + } + + // type + // + if (n.name() == "type" && n.namespace_().empty()) { + if (!type_.present()) { + this->type_.set(type_traits::create(i, f, this)); + continue; + } + } + + // h + // + if (n.name() == "h" && n.namespace_().empty()) { + if (!h_.present()) { + this->h_.set(h_traits::create(i, f, this)); + continue; + } + } + + // mass + // + if (n.name() == "mass" && n.namespace_().empty()) { + if (!mass_.present()) { + this->mass_.set(mass_traits::create(i, f, this)); + continue; + } + } + + // epsilon + // + if (n.name() == "epsilon" && n.namespace_().empty()) { + if (!epsilon_.present()) { + this->epsilon_.set(epsilon_traits::create(i, f, this)); + continue; + } + } + + // sigma + // + if (n.name() == "sigma" && n.namespace_().empty()) { + if (!sigma_.present()) { + this->sigma_.set(sigma_traits::create(i, f, this)); + continue; + } + } + + // mv + // + if (n.name() == "mv" && n.namespace_().empty()) { + if (!mv_.present()) { + this->mv_.set(mv_traits::create(i, f, this)); + continue; + } + } + + break; + } + + if (!velocity_.present()) { + throw ::xsd::cxx::tree::expected_element("velocity", ""); + } + + if (!corner_.present()) { + throw ::xsd::cxx::tree::expected_element("corner", ""); + } + + if (!dimensions_.present()) { + throw ::xsd::cxx::tree::expected_element("dimensions", ""); + } + + if (!type_.present()) { + throw ::xsd::cxx::tree::expected_element("type", ""); + } + + if (!h_.present()) { + throw ::xsd::cxx::tree::expected_element("h", ""); } - if (!force_.present()) { - throw ::xsd::cxx::tree::expected_element("force", ""); + if (!mass_.present()) { + throw ::xsd::cxx::tree::expected_element("mass", ""); } - if (!delta_t_.present()) { - throw ::xsd::cxx::tree::expected_element("delta_t", ""); + if (!epsilon_.present()) { + throw ::xsd::cxx::tree::expected_element("epsilon", ""); } - if (!t_end_.present()) { - throw ::xsd::cxx::tree::expected_element("t_end", ""); + if (!sigma_.present()) { + throw ::xsd::cxx::tree::expected_element("sigma", ""); } - if (!twoD_.present()) { - throw ::xsd::cxx::tree::expected_element("twoD", ""); + if (!mv_.present()) { + throw ::xsd::cxx::tree::expected_element("mv", ""); } } -MetadataType* MetadataType::_clone(::xml_schema::flags f, - ::xml_schema::container* c) const { - return new class MetadataType(*this, f, c); +cuboidType* cuboidType::_clone(::xml_schema::flags f, + ::xml_schema::container* c) const { + return new class cuboidType(*this, f, c); } -MetadataType& MetadataType::operator=(const MetadataType& x) { +cuboidType& cuboidType::operator=(const cuboidType& x) { if (this != &x) { static_cast< ::xml_schema::type&>(*this) = x; - this->container_ = x.container_; - this->force_ = x.force_; - this->delta_t_ = x.delta_t_; - this->t_end_ = x.t_end_; - this->twoD_ = x.twoD_; - this->checkpoint_ = x.checkpoint_; + this->velocity_ = x.velocity_; + this->corner_ = x.corner_; + this->dimensions_ = x.dimensions_; + this->type_ = x.type_; + this->h_ = x.h_; + this->mass_ = x.mass_; + this->epsilon_ = x.epsilon_; + this->sigma_ = x.sigma_; + this->mv_ = x.mv_; } return *this; } -MetadataType::~MetadataType() {} +cuboidType::~cuboidType() {} -// cuboidType +// membraneType // -cuboidType::cuboidType(const velocity_type& velocity, const corner_type& corner, - const dimensions_type& dimensions, const type_type& type, - const h_type& h, const mass_type& mass, - const epsilon_type& epsilon, const sigma_type& sigma, - const mv_type& mv) +membraneType::membraneType(const velocity_type& velocity, + const corner_type& corner, + const dimensions_type& dimensions, + const type_type& type, const h_type& h, + const mass_type& mass, const epsilon_type& epsilon, + const sigma_type& sigma, const mv_type& mv) : ::xml_schema::type(), velocity_(velocity, this), corner_(corner, this), @@ -972,12 +1867,12 @@ cuboidType::cuboidType(const velocity_type& velocity, const corner_type& corner, sigma_(sigma, this), mv_(mv, this) {} -cuboidType::cuboidType(::std::auto_ptr velocity, - ::std::auto_ptr corner, - ::std::auto_ptr dimensions, - const type_type& type, const h_type& h, - const mass_type& mass, const epsilon_type& epsilon, - const sigma_type& sigma, const mv_type& mv) +membraneType::membraneType(::std::auto_ptr velocity, + ::std::auto_ptr corner, + ::std::auto_ptr dimensions, + const type_type& type, const h_type& h, + const mass_type& mass, const epsilon_type& epsilon, + const sigma_type& sigma, const mv_type& mv) : ::xml_schema::type(), velocity_(velocity, this), corner_(corner, this), @@ -989,8 +1884,8 @@ cuboidType::cuboidType(::std::auto_ptr velocity, sigma_(sigma, this), mv_(mv, this) {} -cuboidType::cuboidType(const cuboidType& x, ::xml_schema::flags f, - ::xml_schema::container* c) +membraneType::membraneType(const membraneType& x, ::xml_schema::flags f, + ::xml_schema::container* c) : ::xml_schema::type(x, f, c), velocity_(x.velocity_, f, this), corner_(x.corner_, f, this), @@ -1002,8 +1897,8 @@ cuboidType::cuboidType(const cuboidType& x, ::xml_schema::flags f, sigma_(x.sigma_, f, this), mv_(x.mv_, f, this) {} -cuboidType::cuboidType(const ::xercesc::DOMElement& e, ::xml_schema::flags f, - ::xml_schema::container* c) +membraneType::membraneType(const ::xercesc::DOMElement& e, + ::xml_schema::flags f, ::xml_schema::container* c) : ::xml_schema::type(e, f | ::xml_schema::flags::base, c), velocity_(this), corner_(this), @@ -1020,8 +1915,8 @@ cuboidType::cuboidType(const ::xercesc::DOMElement& e, ::xml_schema::flags f, } } -void cuboidType::parse(::xsd::cxx::xml::dom::parser& p, - ::xml_schema::flags f) { +void membraneType::parse(::xsd::cxx::xml::dom::parser& p, + ::xml_schema::flags f) { for (; p.more_content(); p.next_content(false)) { const ::xercesc::DOMElement& i(p.cur_element()); const ::xsd::cxx::xml::qualified_name n( @@ -1154,12 +2049,12 @@ void cuboidType::parse(::xsd::cxx::xml::dom::parser& p, } } -cuboidType* cuboidType::_clone(::xml_schema::flags f, - ::xml_schema::container* c) const { - return new class cuboidType(*this, f, c); +membraneType* membraneType::_clone(::xml_schema::flags f, + ::xml_schema::container* c) const { + return new class membraneType(*this, f, c); } -cuboidType& cuboidType::operator=(const cuboidType& x) { +membraneType& membraneType::operator=(const membraneType& x) { if (this != &x) { static_cast< ::xml_schema::type&>(*this) = x; this->velocity_ = x.velocity_; @@ -1176,7 +2071,7 @@ cuboidType& cuboidType::operator=(const cuboidType& x) { return *this; } -cuboidType::~cuboidType() {} +membraneType::~membraneType() {} // spheroidType // @@ -2079,21 +2974,30 @@ ForceType::ForceType() : ::xml_schema::type(), Gravity_(this), LennardJones_(this), - SingularGravity_(this) {} + SingularGravity_(this), + HarmonicForce_(this), + IndexForce_(this), + TruncatedLennardJonesForce_(this) {} ForceType::ForceType(const ForceType& x, ::xml_schema::flags f, ::xml_schema::container* c) : ::xml_schema::type(x, f, c), Gravity_(x.Gravity_, f, this), LennardJones_(x.LennardJones_, f, this), - SingularGravity_(x.SingularGravity_, f, this) {} + SingularGravity_(x.SingularGravity_, f, this), + HarmonicForce_(x.HarmonicForce_, f, this), + IndexForce_(x.IndexForce_, f, this), + TruncatedLennardJonesForce_(x.TruncatedLennardJonesForce_, f, this) {} ForceType::ForceType(const ::xercesc::DOMElement& e, ::xml_schema::flags f, ::xml_schema::container* c) : ::xml_schema::type(e, f | ::xml_schema::flags::base, c), Gravity_(this), LennardJones_(this), - SingularGravity_(this) { + SingularGravity_(this), + HarmonicForce_(this), + IndexForce_(this), + TruncatedLennardJonesForce_(this) { if ((f & ::xml_schema::flags::base) == 0) { ::xsd::cxx::xml::dom::parser p(e, true, false, false); this->parse(p, f); @@ -2142,6 +3046,41 @@ void ForceType::parse(::xsd::cxx::xml::dom::parser& p, } } + // HarmonicForce + // + if (n.name() == "HarmonicForce" && n.namespace_().empty()) { + ::std::auto_ptr r( + HarmonicForce_traits::create(i, f, this)); + + if (!this->HarmonicForce_) { + this->HarmonicForce_.set(r); + continue; + } + } + + // IndexForce + // + if (n.name() == "IndexForce" && n.namespace_().empty()) { + ::std::auto_ptr r(IndexForce_traits::create(i, f, this)); + + if (!this->IndexForce_) { + this->IndexForce_.set(r); + continue; + } + } + + // TruncatedLennardJonesForce + // + if (n.name() == "TruncatedLennardJonesForce" && n.namespace_().empty()) { + ::std::auto_ptr r( + TruncatedLennardJonesForce_traits::create(i, f, this)); + + if (!this->TruncatedLennardJonesForce_) { + this->TruncatedLennardJonesForce_.set(r); + continue; + } + } + break; } } @@ -2157,6 +3096,9 @@ ForceType& ForceType::operator=(const ForceType& x) { this->Gravity_ = x.Gravity_; this->LennardJones_ = x.LennardJones_; this->SingularGravity_ = x.SingularGravity_; + this->HarmonicForce_ = x.HarmonicForce_; + this->IndexForce_ = x.IndexForce_; + this->TruncatedLennardJonesForce_ = x.TruncatedLennardJonesForce_; } return *this; @@ -2225,37 +3167,96 @@ LennardJonesForce* LennardJonesForce::_clone(::xml_schema::flags f, LennardJonesForce::~LennardJonesForce() {} +// TruncatedLennardJonesForceType +// + +TruncatedLennardJonesForceType::TruncatedLennardJonesForceType() + : ::xml_schema::type() {} + +TruncatedLennardJonesForceType::TruncatedLennardJonesForceType( + const TruncatedLennardJonesForceType& x, ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(x, f, c) {} + +TruncatedLennardJonesForceType::TruncatedLennardJonesForceType( + const ::xercesc::DOMElement& e, ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(e, f, c) {} + +TruncatedLennardJonesForceType::TruncatedLennardJonesForceType( + const ::xercesc::DOMAttr& a, ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(a, f, c) {} + +TruncatedLennardJonesForceType::TruncatedLennardJonesForceType( + const ::std::string& s, const ::xercesc::DOMElement* e, + ::xml_schema::flags f, ::xml_schema::container* c) + : ::xml_schema::type(s, e, f, c) {} + +TruncatedLennardJonesForceType* TruncatedLennardJonesForceType::_clone( + ::xml_schema::flags f, ::xml_schema::container* c) const { + return new class TruncatedLennardJonesForceType(*this, f, c); +} + +TruncatedLennardJonesForceType::~TruncatedLennardJonesForceType() {} + // SingularGravityType // -SingularGravityType::SingularGravityType() : ::xml_schema::type(), g_(this) {} +SingularGravityType::SingularGravityType(const g_type& g, const axis_type& axis) + : ::xml_schema::type(), g_(g, this), axis_(axis, this) {} SingularGravityType::SingularGravityType(const SingularGravityType& x, ::xml_schema::flags f, ::xml_schema::container* c) - : ::xml_schema::type(x, f, c), g_(x.g_, f, this) {} + : ::xml_schema::type(x, f, c), g_(x.g_, f, this), axis_(x.axis_, f, this) {} SingularGravityType::SingularGravityType(const ::xercesc::DOMElement& e, ::xml_schema::flags f, ::xml_schema::container* c) - : ::xml_schema::type(e, f | ::xml_schema::flags::base, c), g_(this) { + : ::xml_schema::type(e, f | ::xml_schema::flags::base, c), + g_(this), + axis_(this) { if ((f & ::xml_schema::flags::base) == 0) { - ::xsd::cxx::xml::dom::parser p(e, false, false, true); + ::xsd::cxx::xml::dom::parser p(e, true, false, false); this->parse(p, f); } } void SingularGravityType::parse(::xsd::cxx::xml::dom::parser& p, ::xml_schema::flags f) { - while (p.more_attributes()) { - const ::xercesc::DOMAttr& i(p.next_attribute()); + for (; p.more_content(); p.next_content(false)) { + const ::xercesc::DOMElement& i(p.cur_element()); const ::xsd::cxx::xml::qualified_name n( ::xsd::cxx::xml::dom::name(i)); + // g + // if (n.name() == "g" && n.namespace_().empty()) { - this->g_.set(g_traits::create(i, f, this)); - continue; + if (!g_.present()) { + this->g_.set(g_traits::create(i, f, this)); + continue; + } + } + + // axis + // + if (n.name() == "axis" && n.namespace_().empty()) { + if (!axis_.present()) { + this->axis_.set(axis_traits::create(i, f, this)); + continue; + } } + + break; + } + + if (!g_.present()) { + throw ::xsd::cxx::tree::expected_element("g", ""); + } + + if (!axis_.present()) { + throw ::xsd::cxx::tree::expected_element("axis", ""); } } @@ -2269,6 +3270,7 @@ SingularGravityType& SingularGravityType::operator=( if (this != &x) { static_cast< ::xml_schema::type&>(*this) = x; this->g_ = x.g_; + this->axis_ = x.axis_; } return *this; @@ -2276,6 +3278,187 @@ SingularGravityType& SingularGravityType::operator=( SingularGravityType::~SingularGravityType() {} +// HarmonicForceType +// + +HarmonicForceType::HarmonicForceType(const r_0_type& r_0, const k_type& k) + : ::xml_schema::type(), r_0_(r_0, this), k_(k, this) {} + +HarmonicForceType::HarmonicForceType(const HarmonicForceType& x, + ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(x, f, c), r_0_(x.r_0_, f, this), k_(x.k_, f, this) {} + +HarmonicForceType::HarmonicForceType(const ::xercesc::DOMElement& e, + ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(e, f | ::xml_schema::flags::base, c), + r_0_(this), + k_(this) { + if ((f & ::xml_schema::flags::base) == 0) { + ::xsd::cxx::xml::dom::parser p(e, true, false, false); + this->parse(p, f); + } +} + +void HarmonicForceType::parse(::xsd::cxx::xml::dom::parser& p, + ::xml_schema::flags f) { + for (; p.more_content(); p.next_content(false)) { + const ::xercesc::DOMElement& i(p.cur_element()); + const ::xsd::cxx::xml::qualified_name n( + ::xsd::cxx::xml::dom::name(i)); + + // r_0 + // + if (n.name() == "r_0" && n.namespace_().empty()) { + if (!r_0_.present()) { + this->r_0_.set(r_0_traits::create(i, f, this)); + continue; + } + } + + // k + // + if (n.name() == "k" && n.namespace_().empty()) { + if (!k_.present()) { + this->k_.set(k_traits::create(i, f, this)); + continue; + } + } + + break; + } + + if (!r_0_.present()) { + throw ::xsd::cxx::tree::expected_element("r_0", ""); + } + + if (!k_.present()) { + throw ::xsd::cxx::tree::expected_element("k", ""); + } +} + +HarmonicForceType* HarmonicForceType::_clone(::xml_schema::flags f, + ::xml_schema::container* c) const { + return new class HarmonicForceType(*this, f, c); +} + +HarmonicForceType& HarmonicForceType::operator=(const HarmonicForceType& x) { + if (this != &x) { + static_cast< ::xml_schema::type&>(*this) = x; + this->r_0_ = x.r_0_; + this->k_ = x.k_; + } + + return *this; +} + +HarmonicForceType::~HarmonicForceType() {} + +// IndexForceType +// + +IndexForceType::IndexForceType(const time_type& time, + const force_values_type& force_values) + : ::xml_schema::type(), + index_(this), + time_(time, this), + force_values_(force_values, this) {} + +IndexForceType::IndexForceType(const time_type& time, + ::std::auto_ptr force_values) + : ::xml_schema::type(), + index_(this), + time_(time, this), + force_values_(force_values, this) {} + +IndexForceType::IndexForceType(const IndexForceType& x, ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(x, f, c), + index_(x.index_, f, this), + time_(x.time_, f, this), + force_values_(x.force_values_, f, this) {} + +IndexForceType::IndexForceType(const ::xercesc::DOMElement& e, + ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(e, f | ::xml_schema::flags::base, c), + index_(this), + time_(this), + force_values_(this) { + if ((f & ::xml_schema::flags::base) == 0) { + ::xsd::cxx::xml::dom::parser p(e, true, false, false); + this->parse(p, f); + } +} + +void IndexForceType::parse(::xsd::cxx::xml::dom::parser& p, + ::xml_schema::flags f) { + for (; p.more_content(); p.next_content(false)) { + const ::xercesc::DOMElement& i(p.cur_element()); + const ::xsd::cxx::xml::qualified_name n( + ::xsd::cxx::xml::dom::name(i)); + + // index + // + if (n.name() == "index" && n.namespace_().empty()) { + ::std::auto_ptr r(index_traits::create(i, f, this)); + + this->index_.push_back(r); + continue; + } + + // time + // + if (n.name() == "time" && n.namespace_().empty()) { + if (!time_.present()) { + this->time_.set(time_traits::create(i, f, this)); + continue; + } + } + + // force_values + // + if (n.name() == "force_values" && n.namespace_().empty()) { + ::std::auto_ptr r( + force_values_traits::create(i, f, this)); + + if (!force_values_.present()) { + this->force_values_.set(r); + continue; + } + } + + break; + } + + if (!time_.present()) { + throw ::xsd::cxx::tree::expected_element("time", ""); + } + + if (!force_values_.present()) { + throw ::xsd::cxx::tree::expected_element("force_values", ""); + } +} + +IndexForceType* IndexForceType::_clone(::xml_schema::flags f, + ::xml_schema::container* c) const { + return new class IndexForceType(*this, f, c); +} + +IndexForceType& IndexForceType::operator=(const IndexForceType& x) { + if (this != &x) { + static_cast< ::xml_schema::type&>(*this) = x; + this->index_ = x.index_; + this->time_ = x.time_; + this->force_values_ = x.force_values_; + } + + return *this; +} + +IndexForceType::~IndexForceType() {} + // ThermostatType // @@ -2285,7 +3468,8 @@ ThermostatType::ThermostatType(const T_init_type& T_init, T_init_(T_init, this), n_thermostat_(n_thermostat, this), T_target_(this), - deltaT_(this) {} + deltaT_(this), + use_thermal_motion_(this) {} ThermostatType::ThermostatType(const ThermostatType& x, ::xml_schema::flags f, ::xml_schema::container* c) @@ -2293,7 +3477,8 @@ ThermostatType::ThermostatType(const ThermostatType& x, ::xml_schema::flags f, T_init_(x.T_init_, f, this), n_thermostat_(x.n_thermostat_, f, this), T_target_(x.T_target_, f, this), - deltaT_(x.deltaT_, f, this) {} + deltaT_(x.deltaT_, f, this), + use_thermal_motion_(x.use_thermal_motion_, f, this) {} ThermostatType::ThermostatType(const ::xercesc::DOMElement& e, ::xml_schema::flags f, @@ -2302,7 +3487,8 @@ ThermostatType::ThermostatType(const ::xercesc::DOMElement& e, T_init_(this), n_thermostat_(this), T_target_(this), - deltaT_(this) { + deltaT_(this), + use_thermal_motion_(this) { if ((f & ::xml_schema::flags::base) == 0) { ::xsd::cxx::xml::dom::parser p(e, true, false, false); this->parse(p, f); @@ -2352,6 +3538,16 @@ void ThermostatType::parse(::xsd::cxx::xml::dom::parser& p, } } + // use_thermal_motion + // + if (n.name() == "use_thermal_motion" && n.namespace_().empty()) { + if (!this->use_thermal_motion_) { + this->use_thermal_motion_.set( + use_thermal_motion_traits::create(i, f, this)); + continue; + } + } + break; } @@ -2376,6 +3572,7 @@ ThermostatType& ThermostatType::operator=(const ThermostatType& x) { this->n_thermostat_ = x.n_thermostat_; this->T_target_ = x.T_target_; this->deltaT_ = x.deltaT_; + this->use_thermal_motion_ = x.use_thermal_motion_; } return *this; @@ -2391,6 +3588,7 @@ simulation::simulation(const metadata_type& metadata) metadata_(metadata, this), cuboids_(this), spheroids_(this), + membranes_(this), thermostat_(this) {} simulation::simulation(::std::auto_ptr metadata) @@ -2398,6 +3596,7 @@ simulation::simulation(::std::auto_ptr metadata) metadata_(metadata, this), cuboids_(this), spheroids_(this), + membranes_(this), thermostat_(this) {} simulation::simulation(const simulation& x, ::xml_schema::flags f, @@ -2406,6 +3605,7 @@ simulation::simulation(const simulation& x, ::xml_schema::flags f, metadata_(x.metadata_, f, this), cuboids_(x.cuboids_, f, this), spheroids_(x.spheroids_, f, this), + membranes_(x.membranes_, f, this), thermostat_(x.thermostat_, f, this) {} simulation::simulation(const ::xercesc::DOMElement& e, ::xml_schema::flags f, @@ -2414,6 +3614,7 @@ simulation::simulation(const ::xercesc::DOMElement& e, ::xml_schema::flags f, metadata_(this), cuboids_(this), spheroids_(this), + membranes_(this), thermostat_(this) { if ((f & ::xml_schema::flags::base) == 0) { ::xsd::cxx::xml::dom::parser p(e, true, false, false); @@ -2450,13 +3651,24 @@ void simulation::parse(::xsd::cxx::xml::dom::parser& p, } } - // spheroids + // spheroids + // + if (n.name() == "spheroids" && n.namespace_().empty()) { + ::std::auto_ptr r(spheroids_traits::create(i, f, this)); + + if (!this->spheroids_) { + this->spheroids_.set(r); + continue; + } + } + + // membranes // - if (n.name() == "spheroids" && n.namespace_().empty()) { - ::std::auto_ptr r(spheroids_traits::create(i, f, this)); + if (n.name() == "membranes" && n.namespace_().empty()) { + ::std::auto_ptr r(membranes_traits::create(i, f, this)); - if (!this->spheroids_) { - this->spheroids_.set(r); + if (!this->membranes_) { + this->membranes_.set(r); continue; } } @@ -2491,6 +3703,7 @@ simulation& simulation::operator=(const simulation& x) { this->metadata_ = x.metadata_; this->cuboids_ = x.cuboids_; this->spheroids_ = x.spheroids_; + this->membranes_ = x.membranes_; this->thermostat_ = x.thermostat_; } @@ -2607,6 +3820,60 @@ spheroids& spheroids::operator=(const spheroids& x) { spheroids::~spheroids() {} +// membranes +// + +membranes::membranes() : ::xml_schema::type(), membrane_(this) {} + +membranes::membranes(const membranes& x, ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(x, f, c), membrane_(x.membrane_, f, this) {} + +membranes::membranes(const ::xercesc::DOMElement& e, ::xml_schema::flags f, + ::xml_schema::container* c) + : ::xml_schema::type(e, f | ::xml_schema::flags::base, c), membrane_(this) { + if ((f & ::xml_schema::flags::base) == 0) { + ::xsd::cxx::xml::dom::parser p(e, true, false, false); + this->parse(p, f); + } +} + +void membranes::parse(::xsd::cxx::xml::dom::parser& p, + ::xml_schema::flags f) { + for (; p.more_content(); p.next_content(false)) { + const ::xercesc::DOMElement& i(p.cur_element()); + const ::xsd::cxx::xml::qualified_name n( + ::xsd::cxx::xml::dom::name(i)); + + // membrane + // + if (n.name() == "membrane" && n.namespace_().empty()) { + ::std::auto_ptr r(membrane_traits::create(i, f, this)); + + this->membrane_.push_back(r); + continue; + } + + break; + } +} + +membranes* membranes::_clone(::xml_schema::flags f, + ::xml_schema::container* c) const { + return new class membranes(*this, f, c); +} + +membranes& membranes::operator=(const membranes& x) { + if (this != &x) { + static_cast< ::xml_schema::type&>(*this) = x; + this->membrane_ = x.membrane_; + } + + return *this; +} + +membranes::~membranes() {} + #include #include #include @@ -2936,6 +4203,15 @@ void operator<<(::xercesc::DOMElement& e, const MetadataType& i) { s << i.container(); } + // use_c18_strategy + // + if (i.use_c18_strategy()) { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("use_c18_strategy", e)); + + s << *i.use_c18_strategy(); + } + // force // { @@ -2977,6 +4253,81 @@ void operator<<(::xercesc::DOMElement& e, const MetadataType& i) { s << *i.checkpoint(); } + + // statistics + // + if (i.statistics()) { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("statistics", e)); + + s << *i.statistics(); + } +} + +void operator<<(::xercesc::DOMElement& e, const StrategyType& i) { + e << static_cast(i); +} + +void operator<<(::xercesc::DOMAttr&, const StrategyType&) {} + +void operator<<(::xml_schema::list_stream&, const StrategyType&) {} + +void operator<<(::xercesc::DOMElement& e, const CheckpointWrapperType& i) { + e << static_cast(i); + + // name + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("name", e)); + + s << i.name(); + } + + // is_membrane + // + { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("is_membrane", e)); + + s << i.is_membrane(); + } + + // domain + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("domain", e)); + + s << i.domain(); + } +} + +void operator<<(::xercesc::DOMElement& e, const StatisticsType& i) { + e << static_cast(i); + + // x_bins + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("x_bins", e)); + + s << i.x_bins(); + } + + // y_bins + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("y_bins", e)); + + s << i.y_bins(); + } + + // output_interval + // + { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("output_interval", e)); + + s << i.output_interval(); + } } void operator<<(::xercesc::DOMElement& e, const cuboidType& i) { @@ -3058,6 +4409,85 @@ void operator<<(::xercesc::DOMElement& e, const cuboidType& i) { } } +void operator<<(::xercesc::DOMElement& e, const membraneType& i) { + e << static_cast(i); + + // velocity + // + { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("velocity", e)); + + s << i.velocity(); + } + + // corner + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("corner", e)); + + s << i.corner(); + } + + // dimensions + // + { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("dimensions", e)); + + s << i.dimensions(); + } + + // type + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("type", e)); + + s << i.type(); + } + + // h + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("h", e)); + + s << ::xml_schema::as_decimal(i.h()); + } + + // mass + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("mass", e)); + + s << ::xml_schema::as_decimal(i.mass()); + } + + // epsilon + // + { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("epsilon", e)); + + s << ::xml_schema::as_decimal(i.epsilon()); + } + + // sigma + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("sigma", e)); + + s << ::xml_schema::as_decimal(i.sigma()); + } + + // mv + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("mv", e)); + + s << ::xml_schema::as_decimal(i.mv()); + } +} + void operator<<(::xercesc::DOMElement& e, const spheroidType& i) { e << static_cast(i); @@ -3364,6 +4794,33 @@ void operator<<(::xercesc::DOMElement& e, const ForceType& i) { s << *i.SingularGravity(); } + + // HarmonicForce + // + if (i.HarmonicForce()) { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("HarmonicForce", e)); + + s << *i.HarmonicForce(); + } + + // IndexForce + // + if (i.IndexForce()) { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("IndexForce", e)); + + s << *i.IndexForce(); + } + + // TruncatedLennardJonesForce + // + if (i.TruncatedLennardJonesForce()) { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("TruncatedLennardJonesForce", e)); + + s << *i.TruncatedLennardJonesForce(); + } } void operator<<(::xercesc::DOMElement& e, const GravityType& i) { @@ -3382,15 +4839,84 @@ void operator<<(::xercesc::DOMAttr&, const LennardJonesForce&) {} void operator<<(::xml_schema::list_stream&, const LennardJonesForce&) {} +void operator<<(::xercesc::DOMElement& e, + const TruncatedLennardJonesForceType& i) { + e << static_cast(i); +} + +void operator<<(::xercesc::DOMAttr&, const TruncatedLennardJonesForceType&) {} + +void operator<<(::xml_schema::list_stream&, + const TruncatedLennardJonesForceType&) {} + void operator<<(::xercesc::DOMElement& e, const SingularGravityType& i) { e << static_cast(i); // g // - if (i.g()) { - ::xercesc::DOMAttr& a(::xsd::cxx::xml::dom::create_attribute("g", e)); + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("g", e)); + + s << ::xml_schema::as_decimal(i.g()); + } + + // axis + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("axis", e)); + + s << i.axis(); + } +} + +void operator<<(::xercesc::DOMElement& e, const HarmonicForceType& i) { + e << static_cast(i); + + // r_0 + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("r_0", e)); + + s << ::xml_schema::as_decimal(i.r_0()); + } + + // k + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("k", e)); + + s << ::xml_schema::as_decimal(i.k()); + } +} + +void operator<<(::xercesc::DOMElement& e, const IndexForceType& i) { + e << static_cast(i); + + // index + // + for (IndexForceType::index_const_iterator b(i.index().begin()), + n(i.index().end()); + b != n; ++b) { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("index", e)); + + s << *b; + } + + // time + // + { + ::xercesc::DOMElement& s(::xsd::cxx::xml::dom::create_element("time", e)); + + s << ::xml_schema::as_decimal(i.time()); + } + + // force_values + // + { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("force_values", e)); - a << ::xml_schema::as_decimal(*i.g()); + s << i.force_values(); } } @@ -3430,6 +4956,15 @@ void operator<<(::xercesc::DOMElement& e, const ThermostatType& i) { s << ::xml_schema::as_decimal(*i.deltaT()); } + + // use_thermal_motion + // + if (i.use_thermal_motion()) { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("use_thermal_motion", e)); + + s << *i.use_thermal_motion(); + } } void operator<<(::xercesc::DOMElement& e, const simulation& i) { @@ -3462,6 +4997,15 @@ void operator<<(::xercesc::DOMElement& e, const simulation& i) { s << *i.spheroids(); } + // membranes + // + if (i.membranes()) { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("membranes", e)); + + s << *i.membranes(); + } + // thermostat // if (i.thermostat()) { @@ -3501,6 +5045,21 @@ void operator<<(::xercesc::DOMElement& e, const spheroids& i) { } } +void operator<<(::xercesc::DOMElement& e, const membranes& i) { + e << static_cast(i); + + // membrane + // + for (membranes::membrane_const_iterator b(i.membrane().begin()), + n(i.membrane().end()); + b != n; ++b) { + ::xercesc::DOMElement& s( + ::xsd::cxx::xml::dom::create_element("membrane", e)); + + s << *b; + } +} + #include // Begin epilogue. diff --git a/src/io/file/in/xml/input.hxx b/src/io/file/in/xml/input.hxx index 870c8a35..40e6c909 100644 --- a/src/io/file/in/xml/input.hxx +++ b/src/io/file/in/xml/input.hxx @@ -50,7 +50,7 @@ #include #if (XSD_INT_VERSION != 4000000L) -//#error XSD runtime version mismatch +// #error XSD runtime version mismatch #endif #include @@ -241,7 +241,11 @@ const XMLCh* const tree_node_key = ::xsd::cxx::tree::user_data_keys::node; // Forward declarations. // class MetadataType; +class StrategyType; +class CheckpointWrapperType; +class StatisticsType; class cuboidType; +class membraneType; class spheroidType; class Dvec3Type; class Ivec3Type; @@ -253,11 +257,15 @@ class BoundaryConfigType; class ForceType; class GravityType; class LennardJonesForce; +class TruncatedLennardJonesForceType; class SingularGravityType; +class HarmonicForceType; +class IndexForceType; class ThermostatType; class simulation; class cuboids; class spheroids; +class membranes; #include // std::binary_search #include // std::numeric_limits @@ -284,6 +292,22 @@ class MetadataType : public ::xml_schema::type { void container(::std::auto_ptr p); + // use_c18_strategy + // + typedef ::xml_schema::boolean use_c18_strategy_type; + typedef ::xsd::cxx::tree::optional + use_c18_strategy_optional; + typedef ::xsd::cxx::tree::traits + use_c18_strategy_traits; + + const use_c18_strategy_optional& use_c18_strategy() const; + + use_c18_strategy_optional& use_c18_strategy(); + + void use_c18_strategy(const use_c18_strategy_type& x); + + void use_c18_strategy(const use_c18_strategy_optional& x); + // force // typedef ::ForceType force_type; @@ -336,7 +360,7 @@ class MetadataType : public ::xml_schema::type { // checkpoint // - typedef ::xml_schema::string checkpoint_type; + typedef ::CheckpointWrapperType checkpoint_type; typedef ::xsd::cxx::tree::optional checkpoint_optional; typedef ::xsd::cxx::tree::traits checkpoint_traits; @@ -350,6 +374,22 @@ class MetadataType : public ::xml_schema::type { void checkpoint(::std::auto_ptr p); + // statistics + // + typedef ::StatisticsType statistics_type; + typedef ::xsd::cxx::tree::optional statistics_optional; + typedef ::xsd::cxx::tree::traits statistics_traits; + + const statistics_optional& statistics() const; + + statistics_optional& statistics(); + + void statistics(const statistics_type& x); + + void statistics(const statistics_optional& x); + + void statistics(::std::auto_ptr p); + // Constructors. // MetadataType(const container_type&, const force_type&, const delta_t_type&, @@ -378,11 +418,175 @@ class MetadataType : public ::xml_schema::type { protected: ::xsd::cxx::tree::one container_; + use_c18_strategy_optional use_c18_strategy_; ::xsd::cxx::tree::one force_; ::xsd::cxx::tree::one delta_t_; ::xsd::cxx::tree::one t_end_; ::xsd::cxx::tree::one twoD_; checkpoint_optional checkpoint_; + statistics_optional statistics_; +}; + +class StrategyType : public ::xml_schema::type { + public: + // Constructors. + // + StrategyType(); + + StrategyType(const ::xercesc::DOMElement& e, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + StrategyType(const ::xercesc::DOMAttr& a, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + StrategyType(const ::std::string& s, const ::xercesc::DOMElement* e, + ::xml_schema::flags f = 0, ::xml_schema::container* c = 0); + + StrategyType(const StrategyType& x, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + virtual StrategyType* _clone(::xml_schema::flags f = 0, + ::xml_schema::container* c = 0) const; + + virtual ~StrategyType(); +}; + +class CheckpointWrapperType : public ::xml_schema::type { + public: + // name + // + typedef ::xml_schema::string name_type; + typedef ::xsd::cxx::tree::traits name_traits; + + const name_type& name() const; + + name_type& name(); + + void name(const name_type& x); + + void name(::std::auto_ptr p); + + // is_membrane + // + typedef ::xml_schema::boolean is_membrane_type; + typedef ::xsd::cxx::tree::traits is_membrane_traits; + + const is_membrane_type& is_membrane() const; + + is_membrane_type& is_membrane(); + + void is_membrane(const is_membrane_type& x); + + // domain + // + typedef ::Ivec3Type domain_type; + typedef ::xsd::cxx::tree::traits domain_traits; + + const domain_type& domain() const; + + domain_type& domain(); + + void domain(const domain_type& x); + + void domain(::std::auto_ptr p); + + // Constructors. + // + CheckpointWrapperType(const name_type&, const is_membrane_type&, + const domain_type&); + + CheckpointWrapperType(const name_type&, const is_membrane_type&, + ::std::auto_ptr); + + CheckpointWrapperType(const ::xercesc::DOMElement& e, + ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + CheckpointWrapperType(const CheckpointWrapperType& x, + ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + virtual CheckpointWrapperType* _clone(::xml_schema::flags f = 0, + ::xml_schema::container* c = 0) const; + + CheckpointWrapperType& operator=(const CheckpointWrapperType& x); + + virtual ~CheckpointWrapperType(); + + // Implementation. + // + protected: + void parse(::xsd::cxx::xml::dom::parser&, ::xml_schema::flags); + + protected: + ::xsd::cxx::tree::one name_; + ::xsd::cxx::tree::one is_membrane_; + ::xsd::cxx::tree::one domain_; +}; + +class StatisticsType : public ::xml_schema::type { + public: + // x_bins + // + typedef ::xml_schema::int_ x_bins_type; + typedef ::xsd::cxx::tree::traits x_bins_traits; + + const x_bins_type& x_bins() const; + + x_bins_type& x_bins(); + + void x_bins(const x_bins_type& x); + + // y_bins + // + typedef ::xml_schema::int_ y_bins_type; + typedef ::xsd::cxx::tree::traits y_bins_traits; + + const y_bins_type& y_bins() const; + + y_bins_type& y_bins(); + + void y_bins(const y_bins_type& x); + + // output_interval + // + typedef ::xml_schema::int_ output_interval_type; + typedef ::xsd::cxx::tree::traits + output_interval_traits; + + const output_interval_type& output_interval() const; + + output_interval_type& output_interval(); + + void output_interval(const output_interval_type& x); + + // Constructors. + // + StatisticsType(const x_bins_type&, const y_bins_type&, + const output_interval_type&); + + StatisticsType(const ::xercesc::DOMElement& e, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + StatisticsType(const StatisticsType& x, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + virtual StatisticsType* _clone(::xml_schema::flags f = 0, + ::xml_schema::container* c = 0) const; + + StatisticsType& operator=(const StatisticsType& x); + + virtual ~StatisticsType(); + + // Implementation. + // + protected: + void parse(::xsd::cxx::xml::dom::parser&, ::xml_schema::flags); + + protected: + ::xsd::cxx::tree::one x_bins_; + ::xsd::cxx::tree::one y_bins_; + ::xsd::cxx::tree::one output_interval_; }; class cuboidType : public ::xml_schema::type { @@ -543,6 +747,164 @@ class cuboidType : public ::xml_schema::type { ::xsd::cxx::tree::one mv_; }; +class membraneType : public ::xml_schema::type { + public: + // velocity + // + typedef ::Dvec3Type velocity_type; + typedef ::xsd::cxx::tree::traits velocity_traits; + + const velocity_type& velocity() const; + + velocity_type& velocity(); + + void velocity(const velocity_type& x); + + void velocity(::std::auto_ptr p); + + // corner + // + typedef ::Dvec3Type corner_type; + typedef ::xsd::cxx::tree::traits corner_traits; + + const corner_type& corner() const; + + corner_type& corner(); + + void corner(const corner_type& x); + + void corner(::std::auto_ptr p); + + // dimensions + // + typedef ::Ivec3Type dimensions_type; + typedef ::xsd::cxx::tree::traits dimensions_traits; + + const dimensions_type& dimensions() const; + + dimensions_type& dimensions(); + + void dimensions(const dimensions_type& x); + + void dimensions(::std::auto_ptr p); + + // type + // + typedef ::xml_schema::int_ type_type; + typedef ::xsd::cxx::tree::traits type_traits; + + const type_type& type() const; + + type_type& type(); + + void type(const type_type& x); + + // h + // + typedef ::xml_schema::decimal h_type; + typedef ::xsd::cxx::tree::traits + h_traits; + + const h_type& h() const; + + h_type& h(); + + void h(const h_type& x); + + // mass + // + typedef ::xml_schema::decimal mass_type; + typedef ::xsd::cxx::tree::traits + mass_traits; + + const mass_type& mass() const; + + mass_type& mass(); + + void mass(const mass_type& x); + + // epsilon + // + typedef ::xml_schema::decimal epsilon_type; + typedef ::xsd::cxx::tree::traits + epsilon_traits; + + const epsilon_type& epsilon() const; + + epsilon_type& epsilon(); + + void epsilon(const epsilon_type& x); + + // sigma + // + typedef ::xml_schema::decimal sigma_type; + typedef ::xsd::cxx::tree::traits + sigma_traits; + + const sigma_type& sigma() const; + + sigma_type& sigma(); + + void sigma(const sigma_type& x); + + // mv + // + typedef ::xml_schema::decimal mv_type; + typedef ::xsd::cxx::tree::traits + mv_traits; + + const mv_type& mv() const; + + mv_type& mv(); + + void mv(const mv_type& x); + + // Constructors. + // + membraneType(const velocity_type&, const corner_type&, const dimensions_type&, + const type_type&, const h_type&, const mass_type&, + const epsilon_type&, const sigma_type&, const mv_type&); + + membraneType(::std::auto_ptr, ::std::auto_ptr, + ::std::auto_ptr, const type_type&, + const h_type&, const mass_type&, const epsilon_type&, + const sigma_type&, const mv_type&); + + membraneType(const ::xercesc::DOMElement& e, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + membraneType(const membraneType& x, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + virtual membraneType* _clone(::xml_schema::flags f = 0, + ::xml_schema::container* c = 0) const; + + membraneType& operator=(const membraneType& x); + + virtual ~membraneType(); + + // Implementation. + // + protected: + void parse(::xsd::cxx::xml::dom::parser&, ::xml_schema::flags); + + protected: + ::xsd::cxx::tree::one velocity_; + ::xsd::cxx::tree::one corner_; + ::xsd::cxx::tree::one dimensions_; + ::xsd::cxx::tree::one type_; + ::xsd::cxx::tree::one h_; + ::xsd::cxx::tree::one mass_; + ::xsd::cxx::tree::one epsilon_; + ::xsd::cxx::tree::one sigma_; + ::xsd::cxx::tree::one mv_; +}; + class spheroidType : public ::xml_schema::type { public: // velocity @@ -1235,6 +1597,58 @@ class ForceType : public ::xml_schema::type { void SingularGravity(::std::auto_ptr p); + // HarmonicForce + // + typedef ::HarmonicForceType HarmonicForce_type; + typedef ::xsd::cxx::tree::optional HarmonicForce_optional; + typedef ::xsd::cxx::tree::traits + HarmonicForce_traits; + + const HarmonicForce_optional& HarmonicForce() const; + + HarmonicForce_optional& HarmonicForce(); + + void HarmonicForce(const HarmonicForce_type& x); + + void HarmonicForce(const HarmonicForce_optional& x); + + void HarmonicForce(::std::auto_ptr p); + + // IndexForce + // + typedef ::IndexForceType IndexForce_type; + typedef ::xsd::cxx::tree::optional IndexForce_optional; + typedef ::xsd::cxx::tree::traits IndexForce_traits; + + const IndexForce_optional& IndexForce() const; + + IndexForce_optional& IndexForce(); + + void IndexForce(const IndexForce_type& x); + + void IndexForce(const IndexForce_optional& x); + + void IndexForce(::std::auto_ptr p); + + // TruncatedLennardJonesForce + // + typedef ::TruncatedLennardJonesForceType TruncatedLennardJonesForce_type; + typedef ::xsd::cxx::tree::optional + TruncatedLennardJonesForce_optional; + typedef ::xsd::cxx::tree::traits + TruncatedLennardJonesForce_traits; + + const TruncatedLennardJonesForce_optional& TruncatedLennardJonesForce() const; + + TruncatedLennardJonesForce_optional& TruncatedLennardJonesForce(); + + void TruncatedLennardJonesForce(const TruncatedLennardJonesForce_type& x); + + void TruncatedLennardJonesForce(const TruncatedLennardJonesForce_optional& x); + + void TruncatedLennardJonesForce( + ::std::auto_ptr p); + // Constructors. // ForceType(); @@ -1261,6 +1675,9 @@ class ForceType : public ::xml_schema::type { Gravity_optional Gravity_; LennardJones_optional LennardJones_; SingularGravity_optional SingularGravity_; + HarmonicForce_optional HarmonicForce_; + IndexForce_optional IndexForce_; + TruncatedLennardJonesForce_optional TruncatedLennardJonesForce_; }; class GravityType : public ::xml_schema::type { @@ -1311,27 +1728,64 @@ class LennardJonesForce : public ::xml_schema::type { virtual ~LennardJonesForce(); }; +class TruncatedLennardJonesForceType : public ::xml_schema::type { + public: + // Constructors. + // + TruncatedLennardJonesForceType(); + + TruncatedLennardJonesForceType(const ::xercesc::DOMElement& e, + ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + TruncatedLennardJonesForceType(const ::xercesc::DOMAttr& a, + ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + TruncatedLennardJonesForceType(const ::std::string& s, + const ::xercesc::DOMElement* e, + ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + TruncatedLennardJonesForceType(const TruncatedLennardJonesForceType& x, + ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + virtual TruncatedLennardJonesForceType* _clone( + ::xml_schema::flags f = 0, ::xml_schema::container* c = 0) const; + + virtual ~TruncatedLennardJonesForceType(); +}; + class SingularGravityType : public ::xml_schema::type { public: // g // typedef ::xml_schema::decimal g_type; - typedef ::xsd::cxx::tree::optional g_optional; typedef ::xsd::cxx::tree::traits g_traits; - const g_optional& g() const; + const g_type& g() const; - g_optional& g(); + g_type& g(); void g(const g_type& x); - void g(const g_optional& x); + // axis + // + typedef ::xml_schema::int_ axis_type; + typedef ::xsd::cxx::tree::traits axis_traits; + + const axis_type& axis() const; + + axis_type& axis(); + + void axis(const axis_type& x); // Constructors. // - SingularGravityType(); + SingularGravityType(const g_type&, const axis_type&); SingularGravityType(const ::xercesc::DOMElement& e, ::xml_schema::flags f = 0, ::xml_schema::container* c = 0); @@ -1352,7 +1806,135 @@ class SingularGravityType : public ::xml_schema::type { void parse(::xsd::cxx::xml::dom::parser&, ::xml_schema::flags); protected: - g_optional g_; + ::xsd::cxx::tree::one g_; + ::xsd::cxx::tree::one axis_; +}; + +class HarmonicForceType : public ::xml_schema::type { + public: + // r_0 + // + typedef ::xml_schema::decimal r_0_type; + typedef ::xsd::cxx::tree::traits + r_0_traits; + + const r_0_type& r_0() const; + + r_0_type& r_0(); + + void r_0(const r_0_type& x); + + // k + // + typedef ::xml_schema::decimal k_type; + typedef ::xsd::cxx::tree::traits + k_traits; + + const k_type& k() const; + + k_type& k(); + + void k(const k_type& x); + + // Constructors. + // + HarmonicForceType(const r_0_type&, const k_type&); + + HarmonicForceType(const ::xercesc::DOMElement& e, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + HarmonicForceType(const HarmonicForceType& x, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + virtual HarmonicForceType* _clone(::xml_schema::flags f = 0, + ::xml_schema::container* c = 0) const; + + HarmonicForceType& operator=(const HarmonicForceType& x); + + virtual ~HarmonicForceType(); + + // Implementation. + // + protected: + void parse(::xsd::cxx::xml::dom::parser&, ::xml_schema::flags); + + protected: + ::xsd::cxx::tree::one r_0_; + ::xsd::cxx::tree::one k_; +}; + +class IndexForceType : public ::xml_schema::type { + public: + // index + // + typedef ::Ivec3Type index_type; + typedef ::xsd::cxx::tree::sequence index_sequence; + typedef index_sequence::iterator index_iterator; + typedef index_sequence::const_iterator index_const_iterator; + typedef ::xsd::cxx::tree::traits index_traits; + + const index_sequence& index() const; + + index_sequence& index(); + + void index(const index_sequence& s); + + // time + // + typedef ::xml_schema::decimal time_type; + typedef ::xsd::cxx::tree::traits + time_traits; + + const time_type& time() const; + + time_type& time(); + + void time(const time_type& x); + + // force_values + // + typedef ::Dvec3Type force_values_type; + typedef ::xsd::cxx::tree::traits force_values_traits; + + const force_values_type& force_values() const; + + force_values_type& force_values(); + + void force_values(const force_values_type& x); + + void force_values(::std::auto_ptr p); + + // Constructors. + // + IndexForceType(const time_type&, const force_values_type&); + + IndexForceType(const time_type&, ::std::auto_ptr); + + IndexForceType(const ::xercesc::DOMElement& e, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + IndexForceType(const IndexForceType& x, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + virtual IndexForceType* _clone(::xml_schema::flags f = 0, + ::xml_schema::container* c = 0) const; + + IndexForceType& operator=(const IndexForceType& x); + + virtual ~IndexForceType(); + + // Implementation. + // + protected: + void parse(::xsd::cxx::xml::dom::parser&, ::xml_schema::flags); + + protected: + index_sequence index_; + ::xsd::cxx::tree::one time_; + ::xsd::cxx::tree::one force_values_; }; class ThermostatType : public ::xml_schema::type { @@ -1413,6 +1995,22 @@ class ThermostatType : public ::xml_schema::type { void deltaT(const deltaT_optional& x); + // use_thermal_motion + // + typedef ::xml_schema::boolean use_thermal_motion_type; + typedef ::xsd::cxx::tree::optional + use_thermal_motion_optional; + typedef ::xsd::cxx::tree::traits + use_thermal_motion_traits; + + const use_thermal_motion_optional& use_thermal_motion() const; + + use_thermal_motion_optional& use_thermal_motion(); + + void use_thermal_motion(const use_thermal_motion_type& x); + + void use_thermal_motion(const use_thermal_motion_optional& x); + // Constructors. // ThermostatType(const T_init_type&, const n_thermostat_type&); @@ -1440,6 +2038,7 @@ class ThermostatType : public ::xml_schema::type { ::xsd::cxx::tree::one n_thermostat_; T_target_optional T_target_; deltaT_optional deltaT_; + use_thermal_motion_optional use_thermal_motion_; }; class simulation : public ::xml_schema::type { @@ -1489,6 +2088,22 @@ class simulation : public ::xml_schema::type { void spheroids(::std::auto_ptr p); + // membranes + // + typedef ::membranes membranes_type; + typedef ::xsd::cxx::tree::optional membranes_optional; + typedef ::xsd::cxx::tree::traits membranes_traits; + + const membranes_optional& membranes() const; + + membranes_optional& membranes(); + + void membranes(const membranes_type& x); + + void membranes(const membranes_optional& x); + + void membranes(::std::auto_ptr p); + // thermostat // typedef ::ThermostatType thermostat_type; @@ -1533,6 +2148,7 @@ class simulation : public ::xml_schema::type { ::xsd::cxx::tree::one metadata_; cuboids_optional cuboids_; spheroids_optional spheroids_; + membranes_optional membranes_; thermostat_optional thermostat_; }; @@ -1620,6 +2236,48 @@ class spheroids : public ::xml_schema::type { spheroid_sequence spheroid_; }; +class membranes : public ::xml_schema::type { + public: + // membrane + // + typedef ::membraneType membrane_type; + typedef ::xsd::cxx::tree::sequence membrane_sequence; + typedef membrane_sequence::iterator membrane_iterator; + typedef membrane_sequence::const_iterator membrane_const_iterator; + typedef ::xsd::cxx::tree::traits membrane_traits; + + const membrane_sequence& membrane() const; + + membrane_sequence& membrane(); + + void membrane(const membrane_sequence& s); + + // Constructors. + // + membranes(); + + membranes(const ::xercesc::DOMElement& e, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + membranes(const membranes& x, ::xml_schema::flags f = 0, + ::xml_schema::container* c = 0); + + virtual membranes* _clone(::xml_schema::flags f = 0, + ::xml_schema::container* c = 0) const; + + membranes& operator=(const membranes& x); + + virtual ~membranes(); + + // Implementation. + // + protected: + void parse(::xsd::cxx::xml::dom::parser&, ::xml_schema::flags); + + protected: + membrane_sequence membrane_; +}; + #include #include #include @@ -1765,8 +2423,20 @@ void simulation_(::xercesc::DOMDocument& d, const ::simulation& x, void operator<<(::xercesc::DOMElement&, const MetadataType&); +void operator<<(::xercesc::DOMElement&, const StrategyType&); + +void operator<<(::xercesc::DOMAttr&, const StrategyType&); + +void operator<<(::xml_schema::list_stream&, const StrategyType&); + +void operator<<(::xercesc::DOMElement&, const CheckpointWrapperType&); + +void operator<<(::xercesc::DOMElement&, const StatisticsType&); + void operator<<(::xercesc::DOMElement&, const cuboidType&); +void operator<<(::xercesc::DOMElement&, const membraneType&); + void operator<<(::xercesc::DOMElement&, const spheroidType&); void operator<<(::xercesc::DOMElement&, const Dvec3Type&); @@ -1801,8 +2471,19 @@ void operator<<(::xercesc::DOMAttr&, const LennardJonesForce&); void operator<<(::xml_schema::list_stream&, const LennardJonesForce&); +void operator<<(::xercesc::DOMElement&, const TruncatedLennardJonesForceType&); + +void operator<<(::xercesc::DOMAttr&, const TruncatedLennardJonesForceType&); + +void operator<<(::xml_schema::list_stream&, + const TruncatedLennardJonesForceType&); + void operator<<(::xercesc::DOMElement&, const SingularGravityType&); +void operator<<(::xercesc::DOMElement&, const HarmonicForceType&); + +void operator<<(::xercesc::DOMElement&, const IndexForceType&); + void operator<<(::xercesc::DOMElement&, const ThermostatType&); void operator<<(::xercesc::DOMElement&, const simulation&); @@ -1811,6 +2492,8 @@ void operator<<(::xercesc::DOMElement&, const cuboids&); void operator<<(::xercesc::DOMElement&, const spheroids&); +void operator<<(::xercesc::DOMElement&, const membranes&); + #include // Begin epilogue. diff --git a/src/io/file/out/CSVWriter.h b/src/io/file/out/CSVWriter.h new file mode 100644 index 00000000..bf090d08 --- /dev/null +++ b/src/io/file/out/CSVWriter.h @@ -0,0 +1,57 @@ +// +// Created by maximilian on 19.01.25. +// +#pragma once +#include +#include + +#include "utils/SpdWrapper.h" + +/** + * @brief Writes to any csv file + */ +class CSVWriter { + std::ofstream file_; + + public: + /** + * Checks if the given file is open + * @param file_name the target file + */ + explicit CSVWriter(const std::string& file_name) { + SpdWrapper::get()->info("Opening {}...", file_name); + file_.open(file_name); + if (!file_.is_open()) { + SpdWrapper::get()->error("Failed to open CSV output file"); + throw std::ios_base::failure("Failed to open file"); + } + } + + /** + * @brief Destructor + * @note Closes open files + */ + ~CSVWriter() { closeFile(); } + + /** + * @brief writes a vector of data into a csv + * @param time timestamp for data in the 0th column + * @param data the data that is written + */ + void writeLine(const double time, const std::vector& data) { + file_ << time << ","; + for (size_t i = 0; i < data.size(); ++i) { + file_ << data[i]; + if (i < data.size() - 1) { + file_ << ","; + } + } + file_ << "\n"; + file_.flush(); + } + + /** + * @brief closes the specified file + */ + void closeFile() { file_.close(); } +}; diff --git a/src/io/file/out/OutputHelper.h b/src/io/file/out/OutputHelper.h index 662d7389..22d4be51 100644 --- a/src/io/file/out/OutputHelper.h +++ b/src/io/file/out/OutputHelper.h @@ -11,45 +11,45 @@ /** * @brief prints the current state of the system to viewable files - * @param outputDirectory specifies the base for the output folders, on which + * @param output_directory specifies the base for the output folders, on which * other folders named after date are saved * @param iteration the current iteration from the main simulation loop - * @param vtkWriter writes a .vtu a file + * @param vtk_writer writes a .vtu a file * @param particle_container contains the entirety of the current particles */ -inline void plotParticles(const std::string &outputDirectory, +inline void plotParticles(const std::string &output_directory, const int iteration, - outputWriter::VTKWriter &vtkWriter, + outputWriter::VTKWriter &vtk_writer, ParticleContainer &particle_container) { - vtkWriter.initializeOutput(static_cast(particle_container.size())); + vtk_writer.initializeOutput(static_cast(particle_container.size())); particle_container.singleIterator( - [&vtkWriter](const Particle &p) { vtkWriter.plotParticle(p); }); + [&vtk_writer](const Particle &p) { vtk_writer.plotParticle(p); }); - vtkWriter.writeFile(outputDirectory + "/MD_vtk", iteration); + vtk_writer.writeFile(output_directory + "/MD_vtk", iteration); } /** * @brief creates a timestamped directory in ./output/ containing the files at * the currents timestep and the specification used to attain the result. - * @param outputDirectory the output directory named after the date + * @param output_directory the output directory named after the date * @param argc argc passed from main * @param argv argv passed from main * @return name of the output directory path */ -inline std::string createOutputDirectory(const std::string &outputDirectory, +inline std::string createOutputDirectory(const std::string &output_directory, int argc, char *argv[]) { // source for getting time: // https://stackoverflow.com/questions/997946/how-to-get-current-time-and-date-in-c - const auto currentTime = std::chrono::high_resolution_clock::now(); - const std::time_t now = std::chrono::system_clock::to_time_t(currentTime); - const std::tm localTime = *std::localtime(&now); - std::ostringstream timeString; + const auto current_time = std::chrono::high_resolution_clock::now(); + const std::time_t now = std::chrono::system_clock::to_time_t(current_time); + const std::tm local_time = *std::localtime(&now); + std::ostringstream time_string; // output into 'outputDirectory/current_time/' - timeString << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S/"); + time_string << std::put_time(&local_time, "%Y-%m-%d %H:%M:%S/"); const std::filesystem::path output_directory_path = - outputDirectory + timeString.str(); + output_directory + time_string.str(); if (!is_directory(output_directory_path)) { create_directories(output_directory_path); diff --git a/src/io/file/out/VTKWriter.cpp b/src/io/file/out/VTKWriter.cpp index 5e38b464..ae06ab53 100644 --- a/src/io/file/out/VTKWriter.cpp +++ b/src/io/file/out/VTKWriter.cpp @@ -21,8 +21,8 @@ VTKWriter::VTKWriter() = default; VTKWriter::~VTKWriter() = default; -void VTKWriter::initializeOutput(int numParticles) { - vtkFile = new VTKFile_t("UnstructuredGrid"); +void VTKWriter::initializeOutput(int num_particles) { + vtk_file_ = new VTKFile_t("UnstructuredGrid"); // per point, we add type, position, velocity and force PointData pointData; @@ -48,9 +48,9 @@ void VTKWriter::initializeOutput(int numParticles) { cells.DataArray().push_back(cells_data); PieceUnstructuredGrid_t piece(pointData, cellData, points, cells, - numParticles, 0); + num_particles, 0); UnstructuredGrid_t unstructuredGrid(piece); - vtkFile->UnstructuredGrid(unstructuredGrid); + vtk_file_->UnstructuredGrid(unstructuredGrid); } void VTKWriter::writeFile(const std::string &filename, int iteration) const { @@ -59,13 +59,13 @@ void VTKWriter::writeFile(const std::string &filename, int iteration) const { << ".vtu"; std::ofstream file(strstr.str().c_str()); - VTKFile(file, *vtkFile); - delete vtkFile; + VTKFile(file, *vtk_file_); + delete vtk_file_; } void VTKWriter::plotParticle(const Particle &p) const { #ifdef DEBUG - if (vtkFile->UnstructuredGrid().present()) { + if (vtk_file_->UnstructuredGrid().present()) { // DEBUG_PRINT("UnstructuredGrid is present"); } else { DEBUG_PRINT("No UnstructuredGrid present"); @@ -73,7 +73,7 @@ void VTKWriter::plotParticle(const Particle &p) const { #endif PointData::DataArray_sequence &pointDataSequence = - vtkFile->UnstructuredGrid()->Piece().PointData().DataArray(); + vtk_file_->UnstructuredGrid()->Piece().PointData().DataArray(); PointData::DataArray_iterator dataIterator = pointDataSequence.begin(); dataIterator->push_back(p.getM()); @@ -95,7 +95,7 @@ void VTKWriter::plotParticle(const Particle &p) const { dataIterator->push_back(p.getType()); Points::DataArray_sequence &pointsSequence = - vtkFile->UnstructuredGrid()->Piece().Points().DataArray(); + vtk_file_->UnstructuredGrid()->Piece().Points().DataArray(); const Points::DataArray_iterator pointsIterator = pointsSequence.begin(); pointsIterator->push_back(p.getX()[0]); pointsIterator->push_back(p.getX()[1]); diff --git a/src/io/file/out/VTKWriter.h b/src/io/file/out/VTKWriter.h index 66918d6f..e2c385a7 100644 --- a/src/io/file/out/VTKWriter.h +++ b/src/io/file/out/VTKWriter.h @@ -25,7 +25,7 @@ class VTKWriter final { /** * set up internal data structures and prepare to plot a particle. */ - void initializeOutput(int numParticles); + void initializeOutput(int num_particles); /** * plot type, mass, position, velocity and force of a particle. @@ -44,7 +44,7 @@ class VTKWriter final { void writeFile(const std::string &filename, int iteration) const; private: - VTKFile_t *vtkFile{}; + VTKFile_t *vtk_file_{}; }; } // namespace outputWriter diff --git a/src/io/file/out/XmlWriter.cpp b/src/io/file/out/XmlWriter.cpp index 7a890694..aa274836 100644 --- a/src/io/file/out/XmlWriter.cpp +++ b/src/io/file/out/XmlWriter.cpp @@ -3,6 +3,7 @@ // #include "XmlWriter.h" +#include #include #include #include @@ -15,9 +16,9 @@ XmlWriter::XmlWriter() = default; XmlWriter::~XmlWriter() = default; template -inline XmlType wrapVec(const VecType& vec) { - XmlType xmlVec{vec[0], vec[1], vec[2]}; - return xmlVec; +XmlType wrapVec(const VecType& vec) { + XmlType xml_vec{vec[0], vec[1], vec[2]}; + return xml_vec; } ParticleType wrapParticle(const Particle& particle) { @@ -39,6 +40,8 @@ void XmlWriter::writeFile(ParticleContainer& particle_container, const std::string& filepath) { try { ParticlesType xml_particles; + SpdWrapper::get()->info("Writing {} particles to {}", + particle_container.size(), filepath.c_str()); particle_container.singleIterator( [&xml_particles](const Particle& particle) { @@ -48,16 +51,15 @@ void XmlWriter::writeFile(ParticleContainer& particle_container, xml_schema::namespace_infomap map; map[""].name = ""; - map[""].schema = "../../src/io/file/out/checkpoint-schema.xsd"; + map[""].schema = "../src/io/file/out/checkpoint-schema.xsd"; - std::ostringstream fileName; + std::ostringstream file_name; CheckpointType checkpoint{xml_particles}; std::ofstream checkpoint_file(filepath); SpdWrapper::get()->info("--- Written checkpoint to {}", filepath); Checkpoint(checkpoint_file, checkpoint, map); checkpoint_file.close(); - } catch (const std::exception& e) { std::cerr << "Error writing XML file: " << e.what() << "\n"; } diff --git a/src/io/file/out/XmlWriter.h b/src/io/file/out/XmlWriter.h index e2dd46e1..e3a078cf 100644 --- a/src/io/file/out/XmlWriter.h +++ b/src/io/file/out/XmlWriter.h @@ -8,14 +8,17 @@ #include "defs/containers/ParticleContainer.h" /** - * writes particles to a checkpoint file in xml format + * @brief writes particles to a checkpoint file in xml format */ class XmlWriter { public: /** - * constructs an XML Writer + * @brief constructs an XML Writer */ XmlWriter(); + /** + * @brief Destructor for XmlWrither + */ ~XmlWriter(); /** diff --git a/src/utils/ArrayUtils.h b/src/utils/ArrayUtils.h index 263529d9..af7532bd 100644 --- a/src/utils/ArrayUtils.h +++ b/src/utils/ArrayUtils.h @@ -184,6 +184,28 @@ auto L2InnerProduct(const Container &c) { return std::accumulate(std::cbegin(c), std::cend(c), 0.0, [](auto a, auto b) { return a + b * b; }); } + +/** + * @brief returns the signs of the vectors + * @tparam Container + * @param c + * @return vector of length of c containing the sings of the elements + */ +template +auto getSigns(const Container &c) { + std::vector signs; + for (size_t i = 0; i < c.size(); ++i) { + if (c[i] > 0) { + signs[i] = 1; + } else if (c[i] < 0) { + signs[i] = -1; + } else { + signs[i] = 0; + } + } + return signs; +} + } // namespace ArrayUtils /** diff --git a/src/utils/MaxwellBoltzmannDistribution.h b/src/utils/MaxwellBoltzmannDistribution.h index 90e22366..bd712dd4 100644 --- a/src/utils/MaxwellBoltzmannDistribution.h +++ b/src/utils/MaxwellBoltzmannDistribution.h @@ -14,14 +14,14 @@ * Generate a random velocity vector according to the Maxwell-Boltzmann * distribution, with a given average velocity. * - * @param averageVelocity The average velocity of the brownian motion for the + * @param average_velocity The average velocity of the brownian motion for the * system. * @param dimensions Number of dimensions for which the velocity vector shall be * generated. Set this to 2 or 3. * @return Array containing the generated velocity vector. */ inline std::array maxwellBoltzmannDistributedVelocity( - const double averageVelocity, const size_t dimensions) { + const double average_velocity, const size_t dimensions) { // we use a constant seed for repeatability. // random engine needs static lifetime otherwise it would be recreated for // every call. @@ -29,10 +29,10 @@ inline std::array maxwellBoltzmannDistributedVelocity( // when adding independent normally distributed values to all velocity // components the velocity change is maxwell boltzmann distributed - std::normal_distribution normalDistribution{0, 1}; - std::array randomVelocity{}; + std::normal_distribution normal_distribution{0, 1}; + std::array random_velocity{}; for (size_t i = 0; i < dimensions; ++i) { - randomVelocity[i] = averageVelocity * normalDistribution(randomEngine); + random_velocity[i] = average_velocity * normal_distribution(randomEngine); } - return randomVelocity; + return random_velocity; } diff --git a/src/utils/SpdWrapper.cpp b/src/utils/SpdWrapper.cpp index 9b0c51ee..43475388 100644 --- a/src/utils/SpdWrapper.cpp +++ b/src/utils/SpdWrapper.cpp @@ -7,47 +7,50 @@ #include "io/CLArgumentParser.h" -std::shared_ptr SpdWrapper::instance = configure(); +std::shared_ptr SpdWrapper::instance_ = configure(); -std::shared_ptr SpdWrapper::get() { return instance; } +std::shared_ptr SpdWrapper::get() { return instance_; } std::shared_ptr SpdWrapper::configure() { - auto colorConsoleSink = + auto color_console_sink = std::make_shared(); - colorConsoleSink->set_color(spdlog::level::info, colorConsoleSink->blue); - colorConsoleSink->set_color(spdlog::level::warn, colorConsoleSink->yellow); - colorConsoleSink->set_color(spdlog::level::err, colorConsoleSink->red); - colorConsoleSink->set_color(spdlog::level::critical, colorConsoleSink->red); - colorConsoleSink->set_color(spdlog::level::trace, colorConsoleSink->green); + color_console_sink->set_color(spdlog::level::info, color_console_sink->blue); + color_console_sink->set_color(spdlog::level::warn, + color_console_sink->yellow); + color_console_sink->set_color(spdlog::level::err, color_console_sink->red); + color_console_sink->set_color(spdlog::level::critical, + color_console_sink->red); + color_console_sink->set_color(spdlog::level::trace, + color_console_sink->green); spdlog::init_thread_pool(8192, 1); - auto asyncLogger = std::make_shared( - "asyncLogger", spdlog::sinks_init_list{colorConsoleSink}, + auto async_logger = std::make_shared( + "asyncLogger", spdlog::sinks_init_list{color_console_sink}, spdlog::thread_pool(), spdlog::async_overflow_policy::block); - asyncLogger->set_level(spdlog::level::info); - spdlog::register_logger(asyncLogger); - return asyncLogger; + async_logger->set_level(spdlog::level::info); + spdlog::register_logger(async_logger); + return async_logger; } int SpdWrapper::setLogLevel(std::string level) { level = toLower(level); if (level == "trace") { - instance->set_level(spdlog::level::trace); + instance_->set_level(spdlog::level::trace); return 0; } else if (level == "debug") { - instance->set_level(spdlog::level::debug); + instance_->set_level(spdlog::level::debug); return 0; } else if (level == "info") { - instance->set_level(spdlog::level::info); + instance_->set_level(spdlog::level::info); return 0; } else if (level == "warn") { - instance->set_level(spdlog::level::warn); + instance_->set_level(spdlog::level::warn); return 0; } else if (level == "error") { - instance->set_level(spdlog::level::err); + instance_->set_level(spdlog::level::err); return 0; } else if (level == "off") { - instance->set_level(spdlog::level::off); + instance_->set_level(spdlog::level::off); return 0; } diff --git a/src/utils/SpdWrapper.h b/src/utils/SpdWrapper.h index 1bf3edff..bbdcf780 100644 --- a/src/utils/SpdWrapper.h +++ b/src/utils/SpdWrapper.h @@ -39,7 +39,7 @@ class SpdWrapper { /** * @brief holds the singleton instance of the logger */ - static std::shared_ptr instance; + static std::shared_ptr instance_; }; #endif // SPDWRAPPER_H diff --git a/src/utils/Statistics.h b/src/utils/Statistics.h new file mode 100644 index 00000000..03192af7 --- /dev/null +++ b/src/utils/Statistics.h @@ -0,0 +1,111 @@ +// +// Created by maximilian on 19.01.25. +// +#pragma once + +#include + +#include "io/file/out/CSVWriter.h" +#include "utils/ArrayUtils.h" + +/** + * @brief Class used to calculate density and velocity profile + */ +class Statistics { + private: + int x_bins_; + int y_bins_; + CSVWriter density_profile_writer_; + CSVWriter velocity_profile_writer_; + ParticleContainer& container_; + double x_bin_size_; + double y_bin_size_; + double bin_volume_; + + public: + /** + * @brief Initialize Statistics object + * @param x_bins amount of x_bins + * @param y_bins amount of y_bins + * @param container holds the particles + * @param density_profile_output_location path to density csv + * @param velocity_profile_output_location path to velocity csv + */ + Statistics(const int x_bins, const int y_bins, ParticleContainer& container, + const std::string& density_profile_output_location, + const std::string& velocity_profile_output_location) + : x_bins_(x_bins), + y_bins_(y_bins), + density_profile_writer_(density_profile_output_location), + velocity_profile_writer_(velocity_profile_output_location), + container_(container) { + const ivec3 domain = container.getDomain(); + x_bin_size_ = 1.0 * domain[0] / x_bins; + y_bin_size_ = 1.0 * domain[1] / y_bins; + bin_volume_ = x_bin_size_ * y_bin_size_ * domain[2]; + } + + /** + * @brief calculates the density and velocity profile for the bins. + * The bins can also be along the y axis at the same time for a checkerboard + * pattern. The bins are from left to right (x-axis), and for the lines from + * down to up (y-axis) + * @param time current time for indexing + */ + void writeStatistics(const double time) { + std::vector> bins(x_bins_ * y_bins_); + for (const auto p : container_.getParticles()) { + if (p->getType() < 0) + continue; // ignore walls, because their velocity is not strictly set + // to 0 but rather ignored in other calculations + + dvec3 position = p->getX(); + const int x_bin = static_cast(position[0] / x_bin_size_); + const int y_bin = static_cast(position[1] / y_bin_size_); + + bins[x_bin + y_bin * x_bins_].push_back(p); + } + + std::vector density_data; + std::vector velocity_data; + + for (const auto& bin : bins) { + const size_t num_particles = bin.size(); + dvec3 sum_velocity = {0.0, 0.0, 0.0}; + + for (const auto p : bin) { + sum_velocity = sum_velocity + p->getV(); + } + + density_data.push_back( + std::to_string(static_cast(num_particles) / bin_volume_)); + + if (num_particles == 0) { + velocity_data.emplace_back("0.0 0.0 0.0"); + } else { + velocity_data.push_back( + std::to_string(sum_velocity[0] / + static_cast(num_particles)) + + " " + + std::to_string(sum_velocity[1] / + static_cast(num_particles)) + + " " + + std::to_string(sum_velocity[2] / + static_cast(num_particles))); + } + } + + density_profile_writer_.writeLine(time, density_data); + velocity_profile_writer_.writeLine(time, velocity_data); + } + + /** + * @brief Close all open files + */ + void closeFiles() { + density_profile_writer_.closeFile(); + velocity_profile_writer_.closeFile(); + + SpdWrapper::get()->info("Statistics were completed"); + } +}; \ No newline at end of file diff --git a/tests/BoundaryConditionsTest.cpp b/tests/BoundaryConditionsTest.cpp index e1c84320..c80a1e42 100644 --- a/tests/BoundaryConditionsTest.cpp +++ b/tests/BoundaryConditionsTest.cpp @@ -44,67 +44,67 @@ TEST(BoundaryConditions, Precalculations) { dvec3 left_center = {-2, -2, 1}; dvec3 left_bottom = {-2, -2, 1}; - EXPECT_EQ(container.isBoundary_testing( + EXPECT_EQ(container.isBoundaryTesting( container.dvec3ToCellIndex_testing(center_center)), true); - EXPECT_EQ(container.isHalo_testing( + EXPECT_EQ(container.isHaloTesting( container.dvec3ToCellIndex_testing(center_center)), false); - EXPECT_EQ(container.isBoundary_testing( + EXPECT_EQ(container.isBoundaryTesting( container.dvec3ToCellIndex_testing(center_top)), false); EXPECT_EQ( - container.isHalo_testing(container.dvec3ToCellIndex_testing(center_top)), + container.isHaloTesting(container.dvec3ToCellIndex_testing(center_top)), true); - EXPECT_EQ(container.isBoundary_testing( + EXPECT_EQ(container.isBoundaryTesting( container.dvec3ToCellIndex_testing(center_bottom)), false); - EXPECT_EQ(container.isHalo_testing( + EXPECT_EQ(container.isHaloTesting( container.dvec3ToCellIndex_testing(center_bottom)), true); - EXPECT_EQ(container.isBoundary_testing( + EXPECT_EQ(container.isBoundaryTesting( container.dvec3ToCellIndex_testing(right_top)), false); EXPECT_EQ( - container.isHalo_testing(container.dvec3ToCellIndex_testing(right_top)), + container.isHaloTesting(container.dvec3ToCellIndex_testing(right_top)), true); - EXPECT_EQ(container.isBoundary_testing( + EXPECT_EQ(container.isBoundaryTesting( container.dvec3ToCellIndex_testing(right_center)), false); - EXPECT_EQ(container.isHalo_testing( + EXPECT_EQ(container.isHaloTesting( container.dvec3ToCellIndex_testing(right_center)), true); - EXPECT_EQ(container.isBoundary_testing( + EXPECT_EQ(container.isBoundaryTesting( container.dvec3ToCellIndex_testing(right_bottom)), false); - EXPECT_EQ(container.isHalo_testing( + EXPECT_EQ(container.isHaloTesting( container.dvec3ToCellIndex_testing(right_bottom)), true); - EXPECT_EQ(container.isBoundary_testing( + EXPECT_EQ(container.isBoundaryTesting( container.dvec3ToCellIndex_testing(left_top)), false); EXPECT_EQ( - container.isHalo_testing(container.dvec3ToCellIndex_testing(left_top)), + container.isHaloTesting(container.dvec3ToCellIndex_testing(left_top)), true); - EXPECT_EQ(container.isBoundary_testing( + EXPECT_EQ(container.isBoundaryTesting( container.dvec3ToCellIndex_testing(left_center)), false); EXPECT_EQ( - container.isHalo_testing(container.dvec3ToCellIndex_testing(left_center)), + container.isHaloTesting(container.dvec3ToCellIndex_testing(left_center)), true); - EXPECT_EQ(container.isBoundary_testing( + EXPECT_EQ(container.isBoundaryTesting( container.dvec3ToCellIndex_testing(left_bottom)), false); EXPECT_EQ( - container.isHalo_testing(container.dvec3ToCellIndex_testing(left_bottom)), + container.isHaloTesting(container.dvec3ToCellIndex_testing(left_bottom)), true); } @@ -123,13 +123,13 @@ TEST(BoundaryConditions, Idempotence_Outflow) { LinkedCellsConfig::BoundaryType::Outflow, }}); - const Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); + Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); EXPECT_EQ(container.size(), 0) << "Number of Particles is not 0"; container.imposeInvariant(); EXPECT_EQ(container.size(), 0) << "Number of Particles is not 0"; - container.addParticle(p); + container.addParticles({p}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 1"; container.imposeInvariant(); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 1"; @@ -151,8 +151,8 @@ TEST(BoundaryConditions, xhigh_Outflow) { LinkedCellsConfig::BoundaryType::Reflective, }}); - const Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); - container.addParticle(p); + Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); + container.addParticles({p}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; container.singleIterator([this](Particle& p) { p.setX({4, 1, 1}); }); @@ -178,8 +178,8 @@ TEST(BoundaryConditions, xlow_Outflow) { LinkedCellsConfig::BoundaryType::Reflective, }}); - const Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); - container.addParticle(p); + Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); + container.addParticles({p}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; container.singleIterator([this](Particle& p) { p.setX({-1, 1, 1}); }); @@ -205,8 +205,8 @@ TEST(BoundaryConditions, yhigh_Outflow) { LinkedCellsConfig::BoundaryType::Reflective, }}); - const Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); - container.addParticle(p); + Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); + container.addParticles({p}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; container.singleIterator([this](Particle& p) { p.setX({1, 4, 1}); }); @@ -232,8 +232,8 @@ TEST(BoundaryConditions, ylow_Outflow) { LinkedCellsConfig::BoundaryType::Reflective, }}); - const Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); - container.addParticle(p); + Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); + container.addParticles({p}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; container.singleIterator([this](Particle& p) { p.setX({1, -1, 1}); }); @@ -243,6 +243,60 @@ TEST(BoundaryConditions, ylow_Outflow) { EXPECT_EQ(container.size(), 0) << "Particle was not deleted"; } +/** + * test zhigh outflow + */ +TEST(BoundaryConditions, zhigh_Outflow) { + LinkedCellsContainer container( + {.domain = {3, 3, 3}, + .cutoff_radius = 3, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Reflective, + }}); + + Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); + container.addParticles({p}); + EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; + + container.singleIterator([this](Particle& p) { p.setX({1, 1, 4}); }); + EXPECT_EQ(container.size(), 1) << "Number of Particles is not 1"; + + container.imposeInvariant(); + EXPECT_EQ(container.size(), 0) << "Particle was not deleted"; +} + +/** + * test zlow outflow + */ +TEST(BoundaryConditions, zlow_Outflow) { + LinkedCellsContainer container( + {.domain = {3, 3, 3}, + .cutoff_radius = 3, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Outflow, + }}); + + Particle p({1, 1, 1}, {0, 0, 0}, 1, 1, 1); + container.addParticles({p}); + EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; + + container.singleIterator([this](Particle& p) { p.setX({1, 1, -1}); }); + EXPECT_EQ(container.size(), 1) << "Number of Particles is not 1"; + + container.imposeInvariant(); + EXPECT_EQ(container.size(), 0) << "Particle was not deleted"; +} + /** * test xlow Reflective boundary and that no energy is gained */ @@ -259,8 +313,8 @@ TEST(BoundaryConditions, xlow_Reflective) { LinkedCellsConfig::BoundaryType::Outflow, }}); - const Particle p({0.7, 1, 1}, {-1, 0, 0}, 1, 1, 1); - container.addParticle(p); + Particle p({0.7, 1, 1}, {-1, 0, 0}, 1, 1, 1); + container.addParticles({p}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; // simulate 10.000 steps for a specific delta t to assure that it turned around @@ -304,8 +358,8 @@ TEST(BoundaryConditions, xhigh_Reflective) { LinkedCellsConfig::BoundaryType::Outflow, }}); - const Particle p({89.3, 1, 1}, {1, 0, 0}, 1, 1, 1); - container.addParticle(p); + Particle p({89.3, 1, 1}, {1, 0, 0}, 1, 1, 1); + container.addParticles({p}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; // simulate 10.000 steps for a specific delta t to assure that it turned around @@ -349,8 +403,8 @@ TEST(BoundaryConditions, ylow_Reflective) { LinkedCellsConfig::BoundaryType::Outflow, }}); - const Particle p({1, 0.7, 1}, {0, -1, 0}, 1, 1, 1); - container.addParticle(p); + Particle p({1, 0.7, 1}, {0, -1, 0}, 1, 1, 1); + container.addParticles({p}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; // simulate 10.000 steps for a specific delta t to assure that it turned around @@ -394,8 +448,8 @@ TEST(BoundaryConditions, yhigh_Reflective) { LinkedCellsConfig::BoundaryType::Outflow, }}); - const Particle p({1, 89.3, 1}, {0, 1, 0}, 1, 1, 1); - container.addParticle(p); + Particle p({1, 89.3, 1}, {0, 1, 0}, 1, 1, 1); + container.addParticles({p}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; // simulate 10.000 steps for a specific delta t to assure that it turned around @@ -422,3 +476,93 @@ TEST(BoundaryConditions, yhigh_Reflective) { DVEC3_NEAR(p.getV(), {0.0, -1.0, 0.0}, "Violated the law of conservation of energy", 1e-5); }); } + +/** + * test zlow Reflective boundary and that no energy is gained + */ +TEST(BoundaryConditions, zlow_Reflective) { + LinkedCellsContainer container( + {.domain = {3, 90, 3}, // better safe than sorry + .cutoff_radius = 3, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Reflective, + }}); + + Particle p({1, 1, 0.7}, {0, 0, -1}, 1, 1, 1); + container.addParticles({p}); + EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; + + // simulate 10.000 steps for a specific delta t to assure that it turned around + for (int i = 0; i < 10000; i++) { + double delta_t = 0.00005; + container.singleIterator([this, delta_t](Particle& p) { + const dvec3 new_x = p.getX() + delta_t * p.getV() + + (delta_t * delta_t / (2 * p.getM())) * (p.getF()); + p.setX(new_x); + }); + + container.singleIterator([](Particle& p) { p.updateForceInTime(); }); + + container.imposeInvariant(); + + container.singleIterator([this, delta_t](Particle& p) { + const dvec3 new_v = + p.getV() + (delta_t / (2 * p.getM()) * (p.getOldF() + p.getF())); + p.setV(new_v); + }); + } + + container.singleIterator([this](Particle& p) { + DVEC3_NEAR(p.getV(), {0.0, 0, 1.0}, "Violated the law of conservation of energy", 1e-5); + }); +} + +/** + * test zhigh Reflective boundary and that no energy is gained + */ +TEST(BoundaryConditions, zhigh_Reflective) { + LinkedCellsContainer container( + {.domain = {3, 3, 3}, // better safe than sorry + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Outflow, + }}); + + Particle p({1, 1, 2.3}, {0, 0, 1}, 1, 1, 1); + container.addParticles({p}); + EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; + + // simulate 10.000 steps for a specific delta t to assure that it turned around + for (int i = 0; i < 10000; i++) { + double delta_t = 0.00005; + container.singleIterator([this, delta_t](Particle& particle) { + const dvec3 new_x = particle.getX() + delta_t * particle.getV() + + (delta_t * delta_t / (2 * particle.getM())) * (particle.getF()); + particle.setX(new_x); + }); + + container.singleIterator([](Particle& particle) { particle.updateForceInTime(); }); + + container.imposeInvariant(); + + container.singleIterator([this, delta_t](Particle& particle) { + const dvec3 new_v = + particle.getV() + (delta_t / (2 * particle.getM()) * (particle.getOldF() + particle.getF())); + particle.setV(new_v); + }); + } + + container.singleIterator([this](const Particle& particle) { + DVEC3_NEAR(particle.getV(), {0.0, 0.0, -1.0}, "Violated the law of conservation of energy", 1e-5); + }); +} \ No newline at end of file diff --git a/tests/CLArgumentParserTest.cpp b/tests/CLArgumentParserTest.cpp index ddae0104..7fefd787 100644 --- a/tests/CLArgumentParserTest.cpp +++ b/tests/CLArgumentParserTest.cpp @@ -6,7 +6,7 @@ #include "../src/io/CLArgumentParser.h" -/* +/** * @brief Parse example CLI arguments correctly */ TEST(CLArgumentParser, parse) { @@ -27,10 +27,9 @@ TEST(CLArgumentParser, parse) { char* argv[] = {arg0, arg1, arg2, arg3, arg4, arg5, arg6}; - auto [name, step, write_checkpoint] = CLArgumentParser::parse(argc, argv); + auto [name, step, checkpoint_path] = CLArgumentParser::parse(argc, argv); EXPECT_EQ(name, "testFile.txt"); EXPECT_EQ(step, 0.5); EXPECT_EQ(SpdWrapper::get()->level(), spdlog::level::warn); - EXPECT_EQ(write_checkpoint, false); - + EXPECT_FALSE(checkpoint_path); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bc89f6ba..05633e6c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,16 +12,15 @@ file(GLOB_RECURSE MOLSIM_TEST option(BUILD_TESTS "Build tests executable" OFF) if (BUILD_TESTS) + message("BUILD_TESTS is enabled") add_executable(MolSimTest ${MOLSIM_TEST} - CLArgumentParserTest.cpp - DirectSumContainerTest.cpp - LennardJonesTest.cpp - VerletIntegratorTest.cpp BoundaryConditionsTest.cpp - PeriodicBoundaryTest.cpp - testUtil.h XmlReaderTest.cpp - ThermostatTest.cpp) + XmlReaderTest.cpp + TruncatedLennardJones.cpp + IndexForceTest.cpp + SingularGravityTest.cpp + MixingTest.cpp) target_link_libraries(MolSimTest MolSimLib GTest::gtest_main) include(GoogleTest) diff --git a/tests/CheckpointTest.cpp b/tests/CheckpointTest.cpp new file mode 100644 index 00000000..7aa4c97b --- /dev/null +++ b/tests/CheckpointTest.cpp @@ -0,0 +1,148 @@ +// +// Created by jkr on 1/22/25. +// +#include + +#include + +#include "calc/VerletIntegrator.h" +#include "defs/Simulation.h" +#include "defs/containers/DirectSumContainer.h" +#include "defs/containers/LinkedCellsContainer.h" +#include "defs/containers/ParticleContainer.h" +#include "forces/Gravity.h" +#include "forces/LennardJones.h" +#include "io/file/in/xml/XmlReader.h" +#include "io/file/out/XmlWriter.h" +#include "testUtil.h" + +/** + * @brief tests whether the particles written from the checkpoint agree with + * the state of the simulation after 0 iterations + */ +TEST(Checkpoint, cuboid) { + char arg0[] = "./MolSim"; + char arg1[] = "-f"; + char arg2[] = "../../tests/checkpoint_input_test.xml"; + char arg3[] = "-c"; + char arg4[] = "../../input/checkpoint_test.xml"; + char* argv[] = {arg0, arg1, arg2, arg3, arg4}; + auto [name, step, checkpoint_path] = CLArgumentParser::parse(5, argv); + + Arguments arguments; + std::vector particles; + + XmlReader::read(particles, name, arguments); + + LinkedCellsConfig config = {.domain = {300, 300, 1}, + .cutoff_radius = 3.0, + .boundary_config = { + .x_high = LinkedCellsConfig::Outflow, + .x_low = LinkedCellsConfig::Outflow, + .y_high = LinkedCellsConfig::Outflow, + .y_low = LinkedCellsConfig::Outflow, + .z_high = LinkedCellsConfig::Outflow, + .z_low = LinkedCellsConfig::Outflow, + }}; + + const std::unique_ptr container = + std::make_unique(config); + container->addParticles(particles); + container->imposeInvariant(); + + std::cout << particles.size() << " particles" << std::endl; + + XmlWriter::writeFile(*container, checkpoint_path.value()); + + std::cout << "New file trying .... " << std::endl; + char arg01[] = "MolSim"; + char arg11[] = "-f"; + char arg21[] = "../../tests/checkpoint_output_test.xml"; + char* argv1[] = {arg01, arg11, arg21}; + auto [name1, step1, write_checkpoint1] = CLArgumentParser::parse(3, argv1); + + Arguments arguments1; + std::vector particles1; + XmlReader::read(particles1, name1, arguments1); + ASSERT_EQ(particles.size(), particles1.size()); + // Sort to fix cells_ ordering by container + std::sort( + particles.begin(), particles.end(), + [](const Particle& a, const Particle& b) { return a.getX() < b.getX(); }); + std::sort( + particles1.begin(), particles1.end(), + [](const Particle& a, const Particle& b) { return a.getX() < b.getX(); }); + ; + + for (size_t i = 0; i < particles.size(); ++i) { + ASSERT_EQ_VEC3(particles[i].getX(), particles1[i].getX(), + "Vectors not equal at index " + std::to_string(i)); + ASSERT_EQ_VEC3(particles[i].getV(), particles1[i].getV(), + "Vector velocity not near at index " + std::to_string(i)); + } +} + +/** + * Test whether membranes (and especially the particle neighbours) are correctly + * translated into a checkpoint + */ +TEST(Checkpoint, membrane) { + char arg0[] = "./MolSim"; + char arg1[] = "-f"; + char arg2[] = "../../tests/checkpoint_input_membrane_test.xml"; + char arg3[] = "-c"; + char arg4[] = "../../input/checkpoint_membrane_test.xml"; + char* argv[] = {arg0, arg1, arg2, arg3, arg4}; + auto [name, step, checkpoint_path] = CLArgumentParser::parse(5, argv); + + Arguments arguments; + std::vector particles; + + XmlReader::read(particles, name, arguments); + + std::unique_ptr container; + if (std::holds_alternative(arguments.container_data)) { + const LinkedCellsConfig& ld = + std::get(arguments.container_data); + container = std::make_unique(ld); + } + container->addParticles(particles); + container->imposeInvariant(); + + std::cout << particles.size() << " particles" << std::endl; + + XmlWriter::writeFile(*container, checkpoint_path.value()); + + std::cout << "New file trying .... " << std::endl; + char arg01[] = "MolSim"; + char arg11[] = "-f"; + char arg21[] = "../../tests/checkpoint_output_membrane_test.xml"; + char* argv1[] = {arg01, arg11, arg21}; + auto [name1, step1, write_checkpoint1] = CLArgumentParser::parse(3, argv1); + + Arguments arguments1; + std::vector particles1; + XmlReader::read(particles1, name1, arguments1); + + ASSERT_EQ(particles.size(), particles1.size()); + + for (std::size_t i = 0; i < particles.size(); ++i) { + ASSERT_EQ(particles[i].getNeighbours().size(), + particles1[i].getNeighbours().size()); + } + + std::sort( + particles.begin(), particles.end(), + [](const Particle& a, const Particle& b) { return a.getX() < b.getX(); }); + std::sort( + particles1.begin(), particles1.end(), + [](const Particle& a, const Particle& b) { return a.getX() < b.getX(); }); + ; + + for (size_t i = 0; i < particles.size(); ++i) { + ASSERT_EQ_VEC3(particles[i].getX(), particles1[i].getX(), + "Vectors not equal at index " + std::to_string(i)); + ASSERT_EQ_VEC3(particles[i].getV(), particles1[i].getV(), + "Vector velocity not near at index " + std::to_string(i)); + } +} diff --git a/tests/DirectSumContainerTest.cpp b/tests/DirectSumContainerTest.cpp index 771b4272..e5c3d622 100644 --- a/tests/DirectSumContainerTest.cpp +++ b/tests/DirectSumContainerTest.cpp @@ -5,12 +5,12 @@ #include "../src/defs/containers/DirectSumContainer.cpp" #include "testUtil.h" -/* +/** * Add particle changes internal vector length */ TEST(DirectSumContainer, addParticleAndSize) { DirectSumContainer container; - Particle particle; + Particle particle; ASSERT_EQ(container.size(), 0) << "ParticleContainer particle count not 0 after init."; @@ -20,7 +20,7 @@ TEST(DirectSumContainer, addParticleAndSize) { << "ParticleContainer particle count not matching after addParticle."; } -/* +/** * singleIterator iterates over each distinct particle exactly once */ TEST(DirectSumContainer, singleIterator) { @@ -42,7 +42,7 @@ TEST(DirectSumContainer, singleIterator) { EXPECT_VECTOR_EQ(vec, {p1, p2, p3}); } -/* +/** * pair_iterator iterates over each distinct pair of particles exactly once */ TEST(DirectSumContainer, pair_iterator) { diff --git a/tests/IndexForceTest.cpp b/tests/IndexForceTest.cpp new file mode 100644 index 00000000..f23ef487 --- /dev/null +++ b/tests/IndexForceTest.cpp @@ -0,0 +1,43 @@ +// +// Created by jkr on 1/28/25. +// +#include + +#include + +#include "../src/forces/IndexForce.h" +#include "../src/utils/ArrayUtils.h" +#include "debug/debug_print.h" +#include "testUtil.h" + +/** + * TruncatedLennardJones with distance >= sigma * c + */ +TEST(IndexForce, single_index) { + const Particle p({1, 0, 0}, {0, 0, 0}, 1, 5, 1); + const Particle q({0, 1, 0}, {0, 0, 0}, 1, 5, 1); + const Particle r({0, 0, 1}, {0, 0, 0}, 1, 5, 1); + const Particle s({0, 0, 0}, {0, 0, 0}, 1, 5, 1); + const IndexForce index_force{{1}, 10, dvec3{0.8, 0.0, 0.0}}; + double time = 5; + std::vector particles = {p, q, r, s}; + for (Particle &particle : particles) { + DVEC3_NEAR(particle.getF(), {0, 0, 0}, "force not zero"); + } + for (Particle &particle : particles) { + for (const auto index : index_force.getIndices()) { + if (particle.getId() == index) { + particle.addF(index_force.applyForce(particle, time)); + } + } + } + for (Particle &particle : particles) { + for (const auto index : index_force.getIndices()) { + if (particle.getId() == index) { + DVEC3_NEAR(particle.getF(), {0.8, 0, 0}, "force"); + } else { + DVEC3_NEAR(particle.getF(), {0, 0, 0}, "force"); + } + } + } +} \ No newline at end of file diff --git a/tests/LennardJonesTest.cpp b/tests/LennardJonesTest.cpp index a84c6a84..cf995de2 100644 --- a/tests/LennardJonesTest.cpp +++ b/tests/LennardJonesTest.cpp @@ -32,20 +32,6 @@ TEST(LennardJones, directionalForce2) { "Directional force wrong", 1e-5); } -/* - * LennardJones directional force, arbitrary example 3 - */ -/* removed because this never happens in an actual simulation -TEST(LennardJones, directionalForce3) { - Particle p({0, 0, 0}, {0, 0, 0}, 0, 0, 0); - Particle q({0, 0, 0}, {0, 0, 0}, 0, 0, 0); - LennardJones lj; - - dvec3 f = lj.directionalForce(p, q); - DVEC3_NEAR(f, {0.0, 0.0, 0.0}, "Directional force wrong", 1e-5); -} -*/ - /* * LennardJones directional force, arbitrary example 4 */ diff --git a/tests/LinkedCellsContainerTest.cpp b/tests/LinkedCellsContainerTest.cpp index a5ed551e..08683fc6 100644 --- a/tests/LinkedCellsContainerTest.cpp +++ b/tests/LinkedCellsContainerTest.cpp @@ -2,6 +2,7 @@ #include +#include "defs/Generators/CuboidGenerator.h" #include "defs/Simulation.h" #include "defs/containers/LinkedCellsContainer.cpp" #include "defs/containers/LinkedCellsContainer.h" @@ -9,7 +10,7 @@ #include "defs/types.h" #include "testUtil.h" -/* +/** * @brief small helper function to create particles * @param x x coordinate of particle * @param y y coordinate of particle @@ -20,7 +21,7 @@ Particle createParticle(double x, double y, double z) { return Particle({x, y, z}, {0, 0, 0}, 1, 1, 1); } -/* +/** * Container creates the right amount of cells and correct cell dimensions */ TEST(LinkedCellsContainer, constructor) { @@ -46,7 +47,7 @@ TEST(LinkedCellsContainer, constructor) { EXPECT_NEAR(container.getCellDim()[2], 3, 1e-5) << "Z dim wrong."; } -/* +/** * .isBoundary(...) is correct for some examples */ TEST(LinkedCellsContainer, isBoudary) { @@ -78,7 +79,7 @@ TEST(LinkedCellsContainer, isBoudary) { EXPECT_FALSE(container.isBoundary(49)); } -/* +/** * .isHalo(...) is correct for some examples */ TEST(LinkedCellsContainer, isHalo) { @@ -111,7 +112,7 @@ TEST(LinkedCellsContainer, isHalo) { EXPECT_FALSE(container.isHalo(31)); } -/* +/** * .cellIndexToCoord(...) works for some examples * .cellCoordToIndex(...) works for some exmaples * .isValidCellCoordinate(...) works for some examples @@ -161,7 +162,7 @@ TEST(LinkedCellsContainer, << ".isValidCellCoordinate(...) produced wrong result"; } -/* +/** * if new container, then container.size() == 0 * .addParticle(...) increments .size() * .removeParticle(...) decrements .size() @@ -183,19 +184,19 @@ TEST(LinkedCellsContainer, Size_addParticle_and_removeParticle) { << "Freshly instantiated LinkedCellsContainer is not empty."; Particle p = createParticle(1, 1, 1); - container.addParticle(p); + container.addParticles({p}); EXPECT_EQ(container.size(), 1) << ".addParticle() did not increase .size() by 1."; - Particle pr; - container.singleIterator([&pr](Particle& q) { pr = q; }); + Particle pr{{}}; + container.singleIterator([&pr](const Particle& q) { pr = q; }); container.removeParticle(pr); EXPECT_EQ(container.size(), 0) << ".removeParticle() did not decrease .size() by 1."; } -/* +/** * .singleIterator() iterates over all particles */ TEST(LinkedCellsContainer, singleIterator) { @@ -216,56 +217,59 @@ TEST(LinkedCellsContainer, singleIterator) { Particle p2 = createParticle(5, 1, 6); Particle p3 = createParticle(7, 7, 8); - container.addParticle(p1); - container.addParticle(p2); - container.addParticle(p3); + container.addParticles({p1}); + container.addParticles({p2}); + container.addParticles({p3}); EXPECT_EQ(container.size(), 3) << "container particle count not matching after adding 3 particles."; - std::vector vec = {}; - container.singleIterator([&vec](Particle& p) { vec.push_back(p); }); + std::vector vec = {}; + container.singleIterator( + [&vec](const Particle& p) { vec.push_back(p.getId()); }); EXPECT_EQ(vec.size(), 3) << "Single iterator traversed less particles than in the container."; - EXPECT_TRUE(vec[0] == p1 || vec[1] == p1 || vec[2] == p1) + EXPECT_TRUE(vec[0] == p1.getId() || vec[1] == p1.getId() || + vec[2] == p1.getId()) << "Particle was not iterated over."; - EXPECT_TRUE(vec[0] == p2 || vec[1] == p2 || vec[2] == p2) + EXPECT_TRUE(vec[0] == p2.getId() || vec[1] == p2.getId() || + vec[2] == p2.getId()) << "Particle was not iterated over."; - EXPECT_TRUE(vec[0] == p3 || vec[1] == p3 || vec[2] == p3) + EXPECT_TRUE(vec[0] == p3.getId() || vec[1] == p3.getId() || + vec[2] == p3.getId()) << "Particle was not iterated over."; } -/* - Test pairIterator by running the O(n^2) algorithm and checking if - the count of pairs and the pairs themselves match - Note: does not test if all pairs produces are distinct (only matters - if total generated pair count is the same as in reference impl) -*/ +/** + * Test pairIterator by running the O(n^2) algorithm and checking if + * the count of pairs and the pairs themselves match + * Note: does not test if all pairs produces are distinct (only matters + * if total generated pair count is the same as in reference impl) + */ TEST(LinkedCellsContainer, pairIterator) { - LinkedCellsConfig config = {.domain = {10, 10, 10}, - .cutoff_radius = 5, - .boundary_config = { - .x_high = LinkedCellsConfig::Outflow, - .x_low = LinkedCellsConfig::Outflow, - .y_high = LinkedCellsConfig::Outflow, - .y_low = LinkedCellsConfig::Outflow, - .z_high = LinkedCellsConfig::Outflow, - .z_low = LinkedCellsConfig::Outflow, - }}; + constexpr LinkedCellsConfig config = { + .domain = {10, 10, 10}, + .cutoff_radius = 5, + .boundary_config = { + .x_high = LinkedCellsConfig::Outflow, + .x_low = LinkedCellsConfig::Outflow, + .y_high = LinkedCellsConfig::Outflow, + .y_low = LinkedCellsConfig::Outflow, + .z_high = LinkedCellsConfig::Outflow, + .z_low = LinkedCellsConfig::Outflow, + }}; LinkedCellsContainer container(config); - std::array particles = { + std::vector particles = { createParticle(1, 1, 1), createParticle(5, 1, 6), createParticle(7, 7, 8), createParticle(4, 3, 0), createParticle(5, 0, 5), createParticle(1, 5, 2), createParticle(9, 6, 4), createParticle(2, 1, 1), createParticle(3, 0, 0), createParticle(0, 6, 1)}; - for (std::size_t i = 0; i < particles.size(); i++) { - container.addParticle(particles[i]); - } + container.addParticles(particles); EXPECT_EQ(container.size(), particles.size()) << "container particle count not matching after adding 4 particles."; @@ -287,23 +291,25 @@ TEST(LinkedCellsContainer, pairIterator) { } int count = 0; - container.pairIterator([&pairs, &count](Particle& p, Particle& q) { + container.pairIterator([&pairs, &count](const Particle& p, + const Particle& q) { count++; - for (auto it = pairs.begin(); it != pairs.end(); ++it) { - if ((*(*it)[0] == p && *(*it)[1] == q) || - (*(*it)[0] == q && *(*it)[1] == p)) + for (auto& pair : pairs) { + if ((pair[0]->getId() == p.getId() && pair[1]->getId() == q.getId()) || + (pair[0]->getId() == q.getId() && pair[1]->getId() == p.getId())) return; } - EXPECT_TRUE(false) << "Pair Iterator produced a invalid pair"; + FAIL() << "Pair Iterator produced an invalid pair " << p.getId() << " and " + << q.getId(); }); EXPECT_EQ(count, pairs.size()) << "Pair count does not match reference implementation"; } -/* +/** * boundaryIterator(...) goes over all particles in boundary */ TEST(LinkedCellsContainer, boundaryIterator) { @@ -325,18 +331,18 @@ TEST(LinkedCellsContainer, boundaryIterator) { Particle p3 = createParticle(0, 9, 3); Particle p4 = createParticle(4, 4, 4); - container.addParticle(p1); - container.addParticle(p2); - container.addParticle(p3); - container.addParticle(p4); + container.addParticles({p1}); + container.addParticles({p2}); + container.addParticles({p3}); + container.addParticles({p4}); container.boundaryIterator([&p1, &p2, &p3, &p4](Particle& p) { EXPECT_TRUE(p == p1 || p == p2 || p == p3 || !(p == p4)); }); } -/* - * haloIterator(...) goes over all particles in halo +/** + * @brief haloIterator(...) goes over all particles in halo */ TEST(LinkedCellsContainer, haloIterator) { LinkedCellsConfig config = {.domain = {10, 10, 10}, @@ -357,12 +363,128 @@ TEST(LinkedCellsContainer, haloIterator) { Particle p3 = createParticle(0, 11, 0); Particle p4 = createParticle(0, 0, 0); - container.addParticle(p1); - container.addParticle(p2); - container.addParticle(p3); - container.addParticle(p4); + container.addParticles({p1}); + container.addParticles({p2}); + container.addParticles({p3}); + container.addParticles({p4}); container.haloIterator([&p1, &p2, &p3, &p4](Particle& p) { EXPECT_TRUE(p == p1 || p == p2 || p == p3 || !(p == p4)); }); } + +/** + * @brief Tests if the C18 strategy is equal to the standard pair iterator + */ +TEST(LinkedCellsContainer, C18Strategy) { + LinkedCellsConfig config = {.domain = {20, 20, 20}, + .cutoff_radius = 3, + .boundary_config = { + .x_high = LinkedCellsConfig::Outflow, + .x_low = LinkedCellsConfig::Outflow, + .y_high = LinkedCellsConfig::Outflow, + .y_low = LinkedCellsConfig::Outflow, + .z_high = LinkedCellsConfig::Outflow, + .z_low = LinkedCellsConfig::Outflow, + }}; + constexpr double delta_t = 0.001; + LinkedCellsContainer container(config); + std::vector particles; + CuboidGenerator cg({0, 0, 0}, {3, 3, 3}, 1.1225, 1, {0.2, 0.2, 0}, 0.1, 1.0, + 1.0, 1, false); + cg.generate(particles); + container.addParticles(particles); + std::vector> forces; + forces.push_back(std::make_unique()); + + container.imposeInvariant(); + + container.computeInteractiveForcesC18(forces); + + LinkedCellsConfig config2 = {.domain = {20, 20, 20}, + .cutoff_radius = 3, + .boundary_config = { + .x_high = LinkedCellsConfig::Outflow, + .x_low = LinkedCellsConfig::Outflow, + .y_high = LinkedCellsConfig::Outflow, + .y_low = LinkedCellsConfig::Outflow, + .z_high = LinkedCellsConfig::Outflow, + .z_low = LinkedCellsConfig::Outflow, + }}; + + std::vector> forces2; + forces2.push_back(std::make_unique()); + LinkedCellsContainer container2(config2); + std::vector expected; + cg.generate(expected); + container2.addParticles(expected); + container2.imposeInvariant(); + + container2.pairIterator([&forces2](Particle& p, Particle& q) { + dvec3 f = {0, 0, 0}; + for (const auto& force : forces2) { + f = f + force->directionalForce(p, q); + } + p.addF(f); + q.subF(f); + }); + + for (int j = 0; j < particles.size(); j++) { + InfoVec("F: ", container.getParticlesObjects()[j].getF()); + InfoVec("F: ", container2.getParticlesObjects()[j].getF()); + DVEC3_NEAR(container.getParticlesObjects()[j].getF(), + container2.getParticlesObjects()[j].getF(), "F not equal C18", + 10e-13); + } +} + +/** + * Checks if Force Buffer calculates the same result as the standard pair iterator + */ +TEST(LinkedCellsContainer, ForceBufferIteration) { + LinkedCellsConfig config = {.domain = {20, 20, 20}, + .cutoff_radius = 3, + .boundary_config = { + .x_high = LinkedCellsConfig::Outflow, + .x_low = LinkedCellsConfig::Outflow, + .y_high = LinkedCellsConfig::Outflow, + .y_low = LinkedCellsConfig::Outflow, + .z_high = LinkedCellsConfig::Outflow, + .z_low = LinkedCellsConfig::Outflow, + }}; + constexpr double delta_t = 0.001; + LinkedCellsContainer container(config); + std::vector particles; + CuboidGenerator cg({0, 0, 0}, {3, 3, 3}, 1.1225, 1, {0.2, 0.2, 0}, 0.1, 1.0, + 1.0, 1, false); + cg.generate(particles); + container.addParticles(particles); + std::vector> forces; + forces.push_back(std::make_unique()); + + container.imposeInvariant(); + + container.computeInteractiveForcesForceBuffer(forces); + + LinkedCellsContainer container2(config); + std::vector expected; + cg.generate(expected); + container2.addParticles(expected); + + container2.pairIterator([&forces](Particle& p, Particle& q) { + dvec3 f = {0, 0, 0}; + for (const auto& force : forces) { + f = f + force->directionalForce(p, q); + } + p.addF(f); + q.subF(f); + }); + + for (int j = 0; j < particles.size(); j++) { + InfoVec("F: ", container.getParticlesObjects()[j].getF()); + InfoVec("F: ", container2.getParticlesObjects()[j].getF()); + DVEC3_NEAR(container.getParticlesObjects()[j].getF(), + container2.getParticlesObjects()[j].getF(), "F not equal C18", + 10e-13); + } +} \ No newline at end of file diff --git a/tests/MembraneTest.cpp b/tests/MembraneTest.cpp new file mode 100644 index 00000000..133f569a --- /dev/null +++ b/tests/MembraneTest.cpp @@ -0,0 +1,277 @@ +// +// Created by jkr on 12/22/24. +// +#include + +#include +#include + +#include "debug/debug_print.h" +#include "defs/Generators/MembraneGenerator.h" +#include "defs/containers/LinkedCellsContainer.h" +#include "forces/HarmonicForce.h" +#include "forces/IndexForce.h" +#include "forces/InteractiveForce.h" +#include "forces/SingularGravity.h" +#include "forces/TruncatedLennardJones.h" +#include "testUtil.h" +#include "utils/ArrayUtils.h" +/** + * Tests that each particle has the correct number of neighbours for the full + * 3x3 case + */ +TEST(Membrane, 3x3x3) { + std::vector particles; + MembraneGenerator membrane_generator({0, 0, 0}, {3, 3, 3}, 1.1225, 1.0, + {0, 0, 0}, 0.0, 5.0, 1.0, 1, false, {}); + membrane_generator.generate(particles); + + ASSERT_EQ(particles[13].getNeighbours().size(), 26); + ASSERT_EQ(particles[13].getNeighbours()[0].first, true); + ASSERT_EQ(particles[13].getNeighbours()[1].first, true); + ASSERT_EQ(particles[13].getNeighbours()[4].first, false); + + for (auto& particle : particles) { + std::cout << "Address of particle: " << &particle << std::endl; + std::cout << "Number of neighbours: " << particle.getNeighbours().size() + << std::endl; + } +} + +/** + * Test that neighbours get correctly translated to the linkedcellscontainer + * (Technically this is fixed by default constructor) + */ +TEST(Membrane, LC3x3x3) { + constexpr LinkedCellsConfig linked_cells_config = { + .domain = {10, 10, 10}, + .cutoff_radius = 3.0, + .boundary_config = + { + .x_high = LinkedCellsConfig::Outflow, + .x_low = LinkedCellsConfig::Outflow, + .y_high = LinkedCellsConfig::Outflow, + .y_low = LinkedCellsConfig::Outflow, + .z_high = LinkedCellsConfig::Outflow, + .z_low = LinkedCellsConfig::Outflow, + }, + + }; + std::vector particles; + + std::vector> forces; + forces.push_back(std::make_unique(3, 2.2)); + + MembraneGenerator membrane_generator({0, 0, 0}, {3, 3, 3}, 1.1225, 1.0, + {0, 0, 0}, 0.0, 5.0, 1.0, 1, false, {}); + membrane_generator.generate(particles); + + LinkedCellsContainer linked_cells(linked_cells_config); + linked_cells.addParticles(particles); + linked_cells.imposeInvariant(); + for (auto& cell : linked_cells.getCells()) { + for (auto particle : cell) { + SpdWrapper::get()->info("Number of neighbours {}", + particle->getNeighbours().size()); + } + } + double t = 0; + linked_cells.singleIterator([&](Particle& p) { + dvec3 f = {0, 0, 0}; + for (auto& force : forces) { + SpdWrapper::get()->info("Apply force"); + f = f + force->applyForce(p); + } + p.addF(f); + }); +} + +/** + * Test that neighbours get correctly translated to the linkedcellscontainer + * (Technically this is fixed by default constructor) + */ +TEST(Membrane, LC3x3) { + constexpr LinkedCellsConfig linked_cells_config = { + .domain = {10, 10, 10}, + .cutoff_radius = 3.0, + .boundary_config = + { + .x_high = LinkedCellsConfig::Outflow, + .x_low = LinkedCellsConfig::Outflow, + .y_high = LinkedCellsConfig::Outflow, + .y_low = LinkedCellsConfig::Outflow, + .z_high = LinkedCellsConfig::Outflow, + .z_low = LinkedCellsConfig::Outflow, + }, + + }; + std::vector particles; + + std::vector> singular_forces; + singular_forces.push_back(std::make_unique(-0.001, 2)); + singular_forces.push_back(std::make_unique(300, 2.2)); + + std::vector> interactive_forces; + interactive_forces.push_back(std::make_unique()); + + std::vector> index_forces; + IndexForceConfig index_force_config = { + .ids = {5}, + .end_time = 15, + .force_values = {0, 0, 0.8}, + }; + index_forces.push_back(std::make_unique()); + + MembraneGenerator membrane_generator({0, 0, 0}, {3, 3, 1}, 2.2, 1.0, + {0, 0, 0}, 0.0, 1.0, 1.0, 1, true, {}); + membrane_generator.generate(particles); + for (Particle& p : particles) { + SpdWrapper::get()->info("Neighbours: {}", p.getNeighbours().size()); + for (const auto& [fst, snd] : p.getNeighbours()) { + ASSERT_TRUE(snd); + } + } + LinkedCellsContainer linked_cells(linked_cells_config); + linked_cells.addParticles(particles); + linked_cells.imposeInvariant(); + for (auto& cell : linked_cells.getCells()) { + for (auto particle : cell) { + SpdWrapper::get()->info("Number of neighbours {}", + particle->getNeighbours().size()); + for (const auto& [fst, snd] : particle->getNeighbours()) { + ASSERT_TRUE(snd); + } + } + } + double t = 0; + for (int i = 0; i < 5; i++) { + linked_cells.singleIterator([&](Particle& p) { + dvec3 f = {0, 0, 0}; + for (const auto& force : singular_forces) { + SpdWrapper::get()->info("Apply force"); + f = f + force->applyForce(p); + } + InfoVec("total singular forces {}", f); + p.addF(f); + }); + } +} + +/** + * Tests various properties of the membrane structure on 4x4 particles using + * id's, also tests harmonic Force + */ +TEST(Membrane, LC4x4Harmonic) { + constexpr LinkedCellsConfig linked_cells_config = { + .domain = {20, 20, 20}, + .cutoff_radius = 4.0, + .boundary_config = + { + .x_high = LinkedCellsConfig::Outflow, + .x_low = LinkedCellsConfig::Outflow, + .y_high = LinkedCellsConfig::Outflow, + .y_low = LinkedCellsConfig::Outflow, + .z_high = LinkedCellsConfig::Outflow, + .z_low = LinkedCellsConfig::Outflow, + }, + + }; + std::vector particles; + + std::vector> singular_forces; + singular_forces.push_back(std::make_unique(300, 2.2)); + + std::cout << "global id counter " << std::endl; + MembraneGenerator membrane_generator({0, 0, 0}, {4, 4, 1}, 2.5, 1.0, + {0, 0, 0}, 0.0, 1.0, 1.0, 1, true, {}); + + membrane_generator.generate(particles); + + ASSERT_EQ(particles[3].getId(), 3) << "Particle 3 has wrong id"; + ASSERT_EQ(particles[3].getNeighbours().size(), 3) + << "Particle 3 has wrong amount of neighbours"; + + for (int i = 0; i < 3; ++i) { + Particle* neighbourParticle = + reinterpret_cast(particles[3].getNeighbours()[i].second); + int idN = neighbourParticle->getId(); + bool diag = particles[3].getNeighbours()[i].first; + switch (idN) { + case 2: + EXPECT_FALSE(diag) + << "Particle 3 neighbour with id 2 has wrong directionality"; + break; + case 6: + EXPECT_TRUE(diag) + << "Particle 3 neighbour with id 6 has wrong directionality"; + break; + case 7: + EXPECT_FALSE(diag) + << "Particle 3 neighbour with id 7 has wrong directionality"; + break; + default: + FAIL(); + break; + } + } + + for (int i = 0; i < 3; i++) { + Particle* neighbourParticle = + reinterpret_cast(particles[5].getNeighbours()[i].second); + int idN = neighbourParticle->getId(); + + bool diag = particles[5].getNeighbours()[i].first; + switch (idN) { + case 0: + EXPECT_TRUE(diag) + << "Particle 3 neighbour with id 2 has wrong directionality"; + break; + case 1: + EXPECT_FALSE(diag) + << "Particle 3 neighbour with id 6 has wrong directionality"; + break; + case 2: + EXPECT_TRUE(diag) + << "Particle 3 neighbour with id 7 has wrong directionality"; + break; + case 6: + EXPECT_FALSE(diag) + << "Particle 3 neighbour with id 7 has wrong directionality"; + break; + case 10: + EXPECT_TRUE(diag) + << "Particle 3 neighbour with id 7 has wrong directionality"; + break; + case 9: + EXPECT_FALSE(diag) + << "Particle 3 neighbour with id 7 has wrong directionality"; + break; + case 8: + EXPECT_TRUE(diag) + << "Particle 3 neighbour with id 7 has wrong directionality"; + break; + case 4: + EXPECT_FALSE(diag) + << "Particle 3 neighbour with id 4 has wrong directionality"; + break; + default: + FAIL(); + break; + } + } + + DVEC3_NEAR(particles[3].getX(), {0, 7.5, 0}, "Position is wrong", 0.00001); + DVEC3_NEAR(particles[5].getX(), {2.5, 2.5, 0}, "Position o 5 is wrong", + 0.00001); + + dvec3 p1harmonic_force = singular_forces[0]->applyForce(particles[0]); + // [0] -> [1] == (0,90,0) + // [0] -> [4] == (90,0,0) + // [0] -> [5] == (90,90,0) + DVEC3_NEAR(p1harmonic_force, {180, 180, 0}, "Force", 1e-8); + + LinkedCellsContainer linked_cells(linked_cells_config); + linked_cells.addParticles(particles); + linked_cells.singleIterator( + [&](Particle& p) { p.addF(singular_forces[0]->applyForce(p)); }); +} \ No newline at end of file diff --git a/tests/MixingTest.cpp b/tests/MixingTest.cpp new file mode 100644 index 00000000..449a2343 --- /dev/null +++ b/tests/MixingTest.cpp @@ -0,0 +1,36 @@ +#include + +#include + +#include "../src/defs/containers/ParticleContainer.h" +#include "../src/forces/LennardJones.h" +#include "testUtil.h" +#include "../src/utils/ArrayUtils.h" +#include "../src/forces/TruncatedLennardJones.h" + + +/** + * LennardJones directional force, mixing 1 + */ +TEST(LennardJones, mixing) { + Particle p({0, 0, 0}, {0, 0, 0}, 1, 1, 2); + Particle q({2, 2, 2}, {0, 0, 0}, 1, 3, 4); + LennardJones lj; + dvec3 f = lj.directionalForce(p, q); + std::cout << f << std::endl; + DVEC3_NEAR(f, {0.456693, 0.456693, 0.456693}, + "Directional force wrong", 1e-5); +} + +/** + * TruncatedLennardJones directional force, mixing 1 + */ +TEST(TruncatedLennardJones, mixing1) { + Particle p({0, 0, 0}, {0, 0, 0}, 1, 1, 2); + Particle q({2, 2, 2}, {0, 0, 0}, 1, 3, 4); + const TruncatedLennardJones lj; + // cutoff here is 1.1225 * sigma = 1.1225 and dist is sqrt(2) so should be 0 + const dvec3 f = lj.directionalForce(p, q); + std::cout << f << std::endl; + DVEC3_NEAR(f, {0.456693, 0.456693, 0.456693}, "Directional force wrong", 1e-5); +} \ No newline at end of file diff --git a/tests/PeriodicAndReflectiveCornerTest.cpp b/tests/PeriodicAndReflectiveCornerTest.cpp new file mode 100644 index 00000000..fe25498d --- /dev/null +++ b/tests/PeriodicAndReflectiveCornerTest.cpp @@ -0,0 +1,72 @@ +// +// Created by maximilian on 22.01.25. +// +#include + +#include "defs/Particle.h" +#include "defs/containers/LinkedCellsContainer.h" +#include "forces/LennardJones.h" +#include "testUtil.h" +#include "utils/ArrayUtils.h" + +/** + * tests movement through two seperate boundary conditions + */ +TEST(PeriodicAndReflective, XPeriodicYReflective) { + LinkedCellsContainer container( + {.domain = {9, 9, 9}, + .cutoff_radius = 3, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Reflective, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + }}); + + const LennardJones f{}; + + // move to other end + Particle one({8, 8, 1}, {2, 2, 0}, 1, 5.0, 1); // in bb + + container.addParticles({one}); + EXPECT_EQ(container.size(), 1) << "Number of Particles is not 1"; + + // simulate 10.000 steps for a specific delta t to assure that it turned + // around + for (int i = 0; i < 10000; i++) { + double delta_t = 0.0001; + container.singleIterator([this, delta_t](Particle& p) { + const dvec3 new_x = p.getX() + delta_t * p.getV() + + (delta_t * delta_t / (2 * p.getM())) * (p.getF()); + p.setX(new_x); + }); + + container.singleIterator([](Particle& p) { p.updateForceInTime(); }); + + container.imposeInvariant(); + + container.singleIterator([this, delta_t](Particle& p) { + const dvec3 new_v = + p.getV() + (delta_t / (2 * p.getM()) * (p.getOldF() + p.getF())); + p.setV(new_v); + }); + } + + container.singleIterator([this](Particle& p) { + DVEC3_NEAR(p.getV(), {2.0, -2.0, 0}, "wrong velocity", 1e-5); + ASSERT_NEAR(p.getX()[0], 1, 1e-5); + ASSERT_NEAR(p.getX()[2], 1, 1e-5); + // y coordinate can be everything + }); + + // test that particle is registered in its true cell + const auto old_cell = + container.getCells()[container.cellCoordToIndexTesting({2, 2, 0})]; + const auto new_cell = + container.getCells()[container.cellCoordToIndexTesting({0, 2, 0})]; + + EXPECT_EQ(old_cell.size(), 0); + EXPECT_EQ(new_cell.size(), 1); +} \ No newline at end of file diff --git a/tests/PeriodicBoundary3DTest.cpp b/tests/PeriodicBoundary3DTest.cpp new file mode 100644 index 00000000..b0724722 --- /dev/null +++ b/tests/PeriodicBoundary3DTest.cpp @@ -0,0 +1,2238 @@ +// +// Created by maximilian on 22.01.25. +// +#include + +#include "defs/Particle.h" +#include "defs/containers/LinkedCellsContainer.h" +#include "forces/LennardJones.h" +#include "testUtil.h" +#include "utils/ArrayUtils.h" + +/** + * note by the author: this does test ALL combinations for a 3x3 (and therefore + * 5x5) cube. I spent too much time doing this + */ + +/** side view + * +--+--+--+--+--+ + * 3 |ea|eb|ec|ed|ee| + * +--+--+--+--+--+ + * 2 |da|db|dc|dd|de| + * +--+--+--+--+--+ + * 1 |ca|cb|cc|cd|ce| + * +--+--+--+--+--+ + * 0 |ba|bb|bc|bd|be| + * +--+--+--+--+--+ + * -1 |aa|ab|ac|ad|ae| + * +--+--+--+--+--+ + * -1 0 1 2 3 + */ + +//[[======================== warping tests ===================================]] +/// -------------------- [[ 1 boundary periodic ]] -------------------- /// + +/** + * tests warping in 3D for only x periodic + */ +TEST(PeriodicBoundary3D, warpingX) { + const LinkedCellsContainer container( + {.domain = {3, 3, 3}, + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + }}); + bool is_adjacent; + ivec3 new_coordinates; + dvec3 offset; + + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ea"; + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 0}, xhigh); + EXPECT_EQ(is_adjacent, false) << "eb"; + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ec"; + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 2}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ed"; + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ee"; + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "da"; + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 0}, xhigh); + constexpr dvec3 expected_offset11 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset11, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 1}, xhigh); + constexpr dvec3 expected_offset12 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 1}); + DVEC3_NEAR(offset, expected_offset12, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 2}, xhigh); + constexpr dvec3 expected_offset13 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 2}); + DVEC3_NEAR(offset, expected_offset13, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "de"; + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ca"; + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 0}, xhigh); + constexpr dvec3 expected_offset21 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 0}); + DVEC3_NEAR(offset, expected_offset21, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 1}, xhigh); + constexpr dvec3 expected_offset22 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 1}); + DVEC3_NEAR(offset, expected_offset22, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 2}, xhigh); + constexpr dvec3 expected_offset23 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 2}); + DVEC3_NEAR(offset, expected_offset23, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ce"; + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ba"; + + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 0}, xhigh); + constexpr dvec3 expected_offset31 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset31, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 1}, xhigh); + constexpr dvec3 expected_offset32 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 1}); + DVEC3_NEAR(offset, expected_offset32, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 2}, xhigh); + constexpr dvec3 expected_offset33 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset33, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "be"; + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "aa"; + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 0}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ab"; + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ac"; + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 2}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ad"; + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ae"; +} + +/** + * tests warping in 3D for only y periodic + */ +TEST(PeriodicBoundary3D, warpingY) { + const LinkedCellsContainer container( + {.domain = {3, 3, 3}, + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + }}); + bool is_adjacent; + ivec3 new_coordinates; + dvec3 offset; + + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ea"; + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "eb"; + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ec"; + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ed"; + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ee"; + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 2}, yhigh); + EXPECT_EQ(is_adjacent, false) << "da"; + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 2}, yhigh); + constexpr dvec3 expected_offset11 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset11, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 2}, yhigh); + constexpr dvec3 expected_offset12 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 2}); + DVEC3_NEAR(offset, expected_offset12, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 2}, yhigh); + constexpr dvec3 expected_offset13 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 2}); + DVEC3_NEAR(offset, expected_offset13, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 2}, yhigh); + EXPECT_EQ(is_adjacent, false) << "de"; + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ca"; + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 1}, yhigh); + constexpr dvec3 expected_offset21 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 1}); + DVEC3_NEAR(offset, expected_offset21, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 1}, yhigh); + constexpr dvec3 expected_offset22 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 1}); + DVEC3_NEAR(offset, expected_offset22, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 1}, yhigh); + constexpr dvec3 expected_offset23 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 1}); + DVEC3_NEAR(offset, expected_offset23, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ce"; + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 0}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ba"; + + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 0}, yhigh); + constexpr dvec3 expected_offset31 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset31, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 0}, yhigh); + constexpr dvec3 expected_offset32 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 0}); + DVEC3_NEAR(offset, expected_offset32, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 0}, yhigh); + constexpr dvec3 expected_offset33 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 0}); + DVEC3_NEAR(offset, expected_offset33, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 0}, yhigh); + EXPECT_EQ(is_adjacent, false) << "be"; + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "aa"; + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ab"; + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ac"; + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ad"; + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ae"; +} + +/** + * tests warping in 3D for only z periodic + */ +TEST(PeriodicBoundary3D, warpingZ) { + const LinkedCellsContainer container( + {.domain = {3, 3, 3}, + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + }}); + bool is_adjacent; + ivec3 new_coordinates; + dvec3 offset; + + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ea"; + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "eb"; + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ec"; + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ed"; + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ee"; + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "da"; + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 2, 3}, zhigh); + constexpr dvec3 expected_offset11 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset11, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 2, 3}, zhigh); + constexpr dvec3 expected_offset12 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 2, 0}); + DVEC3_NEAR(offset, expected_offset12, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 2, 3}, zhigh); + constexpr dvec3 expected_offset13 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 2, 0}); + DVEC3_NEAR(offset, expected_offset13, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "de"; + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ca"; + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 1, 3}, zhigh); + constexpr dvec3 expected_offset21 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 0}); + DVEC3_NEAR(offset, expected_offset21, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 1, 3}, zhigh); + constexpr dvec3 expected_offset22 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 1, 0}); + DVEC3_NEAR(offset, expected_offset22, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 1, 3}, zhigh); + constexpr dvec3 expected_offset23 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 1, 0}); + DVEC3_NEAR(offset, expected_offset23, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ce"; + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 0, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ba"; + + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 0, 3}, zhigh); + constexpr dvec3 expected_offset31 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset31, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 0, 3}, zhigh); + constexpr dvec3 expected_offset32 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 0}); + DVEC3_NEAR(offset, expected_offset32, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 0, 3}, zhigh); + constexpr dvec3 expected_offset33 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 0}); + DVEC3_NEAR(offset, expected_offset33, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "be"; + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "aa"; + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ab"; + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ac"; + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ad"; + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ae"; +} + +/// -------------------- [[ 2 boundaries periodic ]] -------------------- /// +/** + * tests warping in 3D for only x and y periodic + */ +TEST(PeriodicBoundary3D, warpingXY) { + const LinkedCellsContainer container( + {.domain = {3, 3, 3}, + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + }}); + bool is_adjacent; + ivec3 new_coordinates; + dvec3 offset; + + // on x + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ea"; + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 0}, xhigh); + constexpr dvec3 expected_offset01 = {3, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "eb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset01, "Offset wrong", 1e-5); + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 1}, xhigh); + constexpr dvec3 expected_offset02 = {3, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "ec"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 1}); + DVEC3_NEAR(offset, expected_offset02, "Offset wrong", 1e-5); + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 2}, xhigh); + constexpr dvec3 expected_offset03 = {3, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "ed"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset03, "Offset wrong", 1e-5); + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ee"; + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "da"; + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 0}, xhigh); + constexpr dvec3 expected_offset11 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset11, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 1}, xhigh); + constexpr dvec3 expected_offset12 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 1}); + DVEC3_NEAR(offset, expected_offset12, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 2}, xhigh); + constexpr dvec3 expected_offset13 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 2}); + DVEC3_NEAR(offset, expected_offset13, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "de"; + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ca"; + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 0}, xhigh); + constexpr dvec3 expected_offset21 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 0}); + DVEC3_NEAR(offset, expected_offset21, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 1}, xhigh); + constexpr dvec3 expected_offset22 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 1}); + DVEC3_NEAR(offset, expected_offset22, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 2}, xhigh); + constexpr dvec3 expected_offset23 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 2}); + DVEC3_NEAR(offset, expected_offset23, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ce"; + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ba"; + + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 0}, xhigh); + constexpr dvec3 expected_offset31 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset31, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 1}, xhigh); + constexpr dvec3 expected_offset32 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 1}); + DVEC3_NEAR(offset, expected_offset32, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 2}, xhigh); + constexpr dvec3 expected_offset33 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset33, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "be"; + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "aa"; + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 0}, xhigh); + constexpr dvec3 expected_offset41 = {3, -3, 0}; + EXPECT_EQ(is_adjacent, true) << "ab"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset41, "Offset wrong", 1e-5); + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 1}, xhigh); + constexpr dvec3 expected_offset42 = {3, -3, 0}; + EXPECT_EQ(is_adjacent, true) << "ac"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 1}); + DVEC3_NEAR(offset, expected_offset42, "Offset wrong", 1e-5); + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 2}, xhigh); + constexpr dvec3 expected_offset43 = {3, -3, 0}; + EXPECT_EQ(is_adjacent, true) << "ad"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 2}); + DVEC3_NEAR(offset, expected_offset43, "Offset wrong", 1e-5); + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ae"; + + // --------------------------------------------------------------------------- + // on y + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ea"; + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "eb"; + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ec"; + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ed"; + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ee"; + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 2}, yhigh); + EXPECT_EQ(is_adjacent, false) << "da"; + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 2}, yhigh); + constexpr dvec3 expected_offset111 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset111, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 2}, yhigh); + constexpr dvec3 expected_offset112 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 2}); + DVEC3_NEAR(offset, expected_offset112, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 2}, yhigh); + constexpr dvec3 expected_offset113 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 2}); + DVEC3_NEAR(offset, expected_offset113, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 2}, yhigh); + EXPECT_EQ(is_adjacent, false) << "de"; + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ca"; + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 1}, yhigh); + constexpr dvec3 expected_offset121 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 1}); + DVEC3_NEAR(offset, expected_offset121, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 1}, yhigh); + constexpr dvec3 expected_offset122 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 1}); + DVEC3_NEAR(offset, expected_offset122, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 1}, yhigh); + constexpr dvec3 expected_offset123 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 1}); + DVEC3_NEAR(offset, expected_offset123, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ce"; + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 0}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ba"; + + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 0}, yhigh); + constexpr dvec3 expected_offset131 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset131, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 0}, yhigh); + constexpr dvec3 expected_offset132 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 0}); + DVEC3_NEAR(offset, expected_offset132, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 0}, yhigh); + constexpr dvec3 expected_offset133 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 0}); + DVEC3_NEAR(offset, expected_offset133, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 0}, yhigh); + EXPECT_EQ(is_adjacent, false) << "be"; + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "aa"; + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ab"; + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ac"; + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ad"; + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ae"; +} + +/** + * tests warping in 3D for only x and z periodic + */ +TEST(PeriodicBoundary3D, warpingXZ) { + const LinkedCellsContainer container( + {.domain = {3, 3, 3}, + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + }}); + bool is_adjacent; + ivec3 new_coordinates; + dvec3 offset; + + // on x + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ea"; + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 0}, xhigh); + EXPECT_EQ(is_adjacent, false) << "eb"; + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ec"; + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 2}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ed"; + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ee"; + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, -1}, xhigh); + constexpr dvec3 expected_offset10 = {3, 0, -3}; + EXPECT_EQ(is_adjacent, true) << "da"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 2}); + DVEC3_NEAR(offset, expected_offset10, "Offset wrong", 1e-5); + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 0}, xhigh); + constexpr dvec3 expected_offset11 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset11, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 1}, xhigh); + constexpr dvec3 expected_offset12 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 1}); + DVEC3_NEAR(offset, expected_offset12, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 2}, xhigh); + constexpr dvec3 expected_offset13 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 2}); + DVEC3_NEAR(offset, expected_offset13, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 3}, xhigh); + constexpr dvec3 expected_offset14 = {3, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "de"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset14, "Offset wrong", 1e-5); + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, -1}, xhigh); + constexpr dvec3 expected_offset20 = {3, 0, -3}; + EXPECT_EQ(is_adjacent, true) << "ca"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 2}); + DVEC3_NEAR(offset, expected_offset20, "Offset wrong", 1e-5); + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 0}, xhigh); + constexpr dvec3 expected_offset21 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 0}); + DVEC3_NEAR(offset, expected_offset21, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 1}, xhigh); + constexpr dvec3 expected_offset22 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 1}); + DVEC3_NEAR(offset, expected_offset22, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 2}, xhigh); + constexpr dvec3 expected_offset23 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 2}); + DVEC3_NEAR(offset, expected_offset23, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 3}, xhigh); + constexpr dvec3 expected_offset24 = {3, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "ce"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 0}); + DVEC3_NEAR(offset, expected_offset24, "Offset wrong", 1e-5); + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, -1}, xhigh); + constexpr dvec3 expected_offset30 = {3, 0, -3}; + EXPECT_EQ(is_adjacent, true) << "ba"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset30, "Offset wrong", 1e-5); + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 0}, xhigh); + constexpr dvec3 expected_offset31 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset31, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 1}, xhigh); + constexpr dvec3 expected_offset32 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 1}); + DVEC3_NEAR(offset, expected_offset32, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 2}, xhigh); + constexpr dvec3 expected_offset33 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset33, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 3}, xhigh); + constexpr dvec3 expected_offset34 = {3, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "be"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset34, "Offset wrong", 1e-5); + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, -1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "aa"; + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 0}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ab"; + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 1}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ac"; + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 2}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ad"; + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 3}, xhigh); + EXPECT_EQ(is_adjacent, false) << "ae"; + + // --------------------------------------------------------------------------- + // on z + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ea"; + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "eb"; + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ec"; + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ed"; + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ee"; + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "da"; + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 2, 3}, zhigh); + constexpr dvec3 expected_offset111 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset111, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 2, 3}, zhigh); + constexpr dvec3 expected_offset112 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 2, 0}); + DVEC3_NEAR(offset, expected_offset112, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 2, 3}, zhigh); + constexpr dvec3 expected_offset113 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 2, 0}); + DVEC3_NEAR(offset, expected_offset113, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "de"; + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ca"; + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 1, 3}, zhigh); + constexpr dvec3 expected_offset121 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 0}); + DVEC3_NEAR(offset, expected_offset121, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 1, 3}, zhigh); + constexpr dvec3 expected_offset122 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 1, 0}); + DVEC3_NEAR(offset, expected_offset122, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 1, 3}, zhigh); + constexpr dvec3 expected_offset123 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 1, 0}); + DVEC3_NEAR(offset, expected_offset123, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ce"; + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 0, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ba"; + + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 0, 3}, zhigh); + constexpr dvec3 expected_offset131 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset131, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 0, 3}, zhigh); + constexpr dvec3 expected_offset132 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 0}); + DVEC3_NEAR(offset, expected_offset132, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 0, 3}, zhigh); + constexpr dvec3 expected_offset133 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 0}); + DVEC3_NEAR(offset, expected_offset133, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "be"; + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "aa"; + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ab"; + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ac"; + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ad"; + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ae"; +} + +/** + * tests warping in 3D for only y and z periodic + */ +TEST(PeriodicBoundary3D, warpingYZ) { + const LinkedCellsContainer container( + {.domain = {3, 3, 3}, + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + }}); + bool is_adjacent; + ivec3 new_coordinates; + dvec3 offset; + + // on y + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ea"; + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 3}, yhigh); + constexpr dvec3 expected_offset00011 = {0, 3, 3}; + EXPECT_EQ(is_adjacent, true) << "eb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset00011, "Offset wrong", 1e-5); + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 3}, yhigh); + constexpr dvec3 expected_offset011 = {0, 3, 3}; + EXPECT_EQ(is_adjacent, true) << "ec"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 0}); + DVEC3_NEAR(offset, expected_offset011, "Offset wrong", 1e-5); + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 3}, yhigh); + constexpr dvec3 expected_offset0011 = {0, 3, 3}; + EXPECT_EQ(is_adjacent, true) << "ed"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 0}); + DVEC3_NEAR(offset, expected_offset0011, "Offset wrong", 1e-5); + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ee"; + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 2}, yhigh); + EXPECT_EQ(is_adjacent, false) << "da"; + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 2}, yhigh); + constexpr dvec3 expected_offset11 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset11, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 2}, yhigh); + constexpr dvec3 expected_offset12 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 2}); + DVEC3_NEAR(offset, expected_offset12, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 2}, yhigh); + constexpr dvec3 expected_offset13 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 2}); + DVEC3_NEAR(offset, expected_offset13, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 2}, yhigh); + EXPECT_EQ(is_adjacent, false) << "de"; + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ca"; + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 1}, yhigh); + constexpr dvec3 expected_offset21 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 1}); + DVEC3_NEAR(offset, expected_offset21, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 1}, yhigh); + constexpr dvec3 expected_offset22 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 1}); + DVEC3_NEAR(offset, expected_offset22, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 1}, yhigh); + constexpr dvec3 expected_offset23 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 1}); + DVEC3_NEAR(offset, expected_offset23, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ce"; + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 0}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ba"; + + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 0}, yhigh); + constexpr dvec3 expected_offset31 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset31, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 0}, yhigh); + constexpr dvec3 expected_offset32 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 0}); + DVEC3_NEAR(offset, expected_offset32, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 0}, yhigh); + constexpr dvec3 expected_offset33 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 0}); + DVEC3_NEAR(offset, expected_offset33, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 0}, yhigh); + EXPECT_EQ(is_adjacent, false) << "be"; + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "aa"; + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, -1}, yhigh); + constexpr dvec3 expected_offset110000 = {0, 3, -3}; + EXPECT_EQ(is_adjacent, true) << "ab"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset110000, "Offset wrong", 1e-5); + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, -1}, yhigh); + constexpr dvec3 expected_offset11200 = {0, 3, -3}; + EXPECT_EQ(is_adjacent, true) << "ac"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 2}); + DVEC3_NEAR(offset, expected_offset11200, "Offset wrong", 1e-5); + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, -1}, yhigh); + constexpr dvec3 expected_offset111000 = {0, 3, -3}; + EXPECT_EQ(is_adjacent, true) << "ad"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 2}); + DVEC3_NEAR(offset, expected_offset111000, "Offset wrong", 1e-5); + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ae"; + + // --------------------------------------------------------------------------- + // on z + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ea"; + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "eb"; + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ec"; + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ed"; + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ee"; + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "da"; + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 2, 3}, zhigh); + constexpr dvec3 expected_offset111 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset111, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 2, 3}, zhigh); + constexpr dvec3 expected_offset112 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 2, 0}); + DVEC3_NEAR(offset, expected_offset112, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 2, 3}, zhigh); + constexpr dvec3 expected_offset113 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 2, 0}); + DVEC3_NEAR(offset, expected_offset113, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "de"; + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ca"; + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 1, 3}, zhigh); + constexpr dvec3 expected_offset121 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 0}); + DVEC3_NEAR(offset, expected_offset121, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 1, 3}, zhigh); + constexpr dvec3 expected_offset122 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 1, 0}); + DVEC3_NEAR(offset, expected_offset122, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 1, 3}, zhigh); + constexpr dvec3 expected_offset123 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 1, 0}); + DVEC3_NEAR(offset, expected_offset123, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ce"; + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 0, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ba"; + + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 0, 3}, zhigh); + constexpr dvec3 expected_offset131 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset131, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 0, 3}, zhigh); + constexpr dvec3 expected_offset132 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 0}); + DVEC3_NEAR(offset, expected_offset132, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 0, 3}, zhigh); + constexpr dvec3 expected_offset133 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 0}); + DVEC3_NEAR(offset, expected_offset133, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "be"; + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "aa"; + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ab"; + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ac"; + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ad"; + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ae"; +} + +/** + * tests warping in 3D for x, y and z periodic + */ +TEST(PeriodicBoundary3D, warpingXYZ) { + const LinkedCellsContainer container( + {.domain = {3, 3, 3}, + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + }}); + bool is_adjacent; + ivec3 new_coordinates; + dvec3 offset; + + // on x + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, -1}, xhigh); + constexpr dvec3 expected_offset801 = {3, 3, -3}; + EXPECT_EQ(is_adjacent, true) << "ea"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset801, "Offset wrong", 1e-5); + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 0}, xhigh); + constexpr dvec3 expected_offset01 = {3, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "eb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset01, "Offset wrong", 1e-5); + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 1}, xhigh); + constexpr dvec3 expected_offset02 = {3, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "ec"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 1}); + DVEC3_NEAR(offset, expected_offset02, "Offset wrong", 1e-5); + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 2}, xhigh); + constexpr dvec3 expected_offset03 = {3, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "ed"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset03, "Offset wrong", 1e-5); + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, xhigh); + constexpr dvec3 expected_offset803 = {3, 3, 3}; + EXPECT_EQ(is_adjacent, true) << "ee"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset803, "Offset wrong", 1e-5); + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, -1}, xhigh); + constexpr dvec3 expected_offset811 = {3, 0, -3}; + EXPECT_EQ(is_adjacent, true) << "da"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 2}); + DVEC3_NEAR(offset, expected_offset811, "Offset wrong", 1e-5); + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 0}, xhigh); + constexpr dvec3 expected_offset11 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset11, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 1}, xhigh); + constexpr dvec3 expected_offset12 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 1}); + DVEC3_NEAR(offset, expected_offset12, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 2}, xhigh); + constexpr dvec3 expected_offset13 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 2}); + DVEC3_NEAR(offset, expected_offset13, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 3}, xhigh); + constexpr dvec3 expected_offset813 = {3, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "de"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset813, "Offset wrong", 1e-5); + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, -1}, xhigh); + constexpr dvec3 expected_offset821 = {3, 0, -3}; + EXPECT_EQ(is_adjacent, true) << "ca"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 2}); + DVEC3_NEAR(offset, expected_offset821, "Offset wrong", 1e-5); + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 0}, xhigh); + constexpr dvec3 expected_offset21 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 0}); + DVEC3_NEAR(offset, expected_offset21, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 1}, xhigh); + constexpr dvec3 expected_offset22 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 1}); + DVEC3_NEAR(offset, expected_offset22, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 2}, xhigh); + constexpr dvec3 expected_offset23 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 2}); + DVEC3_NEAR(offset, expected_offset23, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 3}, xhigh); + constexpr dvec3 expected_offset823 = {3, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "ce"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 0}); + DVEC3_NEAR(offset, expected_offset823, "Offset wrong", 1e-5); + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, -1}, xhigh); + constexpr dvec3 expected_offset831 = {3, 0, -3}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset831, "Offset wrong", 1e-5); + + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 0}, xhigh); + constexpr dvec3 expected_offset31 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset31, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 1}, xhigh); + constexpr dvec3 expected_offset32 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 1}); + DVEC3_NEAR(offset, expected_offset32, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 2}, xhigh); + constexpr dvec3 expected_offset33 = {3, 0, 0}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset33, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 3}, xhigh); + constexpr dvec3 expected_offset833 = {3, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "be"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset833, "Offset wrong", 1e-5); + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, -1}, xhigh); + constexpr dvec3 expected_offset841 = {3, -3, -3}; + EXPECT_EQ(is_adjacent, true) << "aa"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 2}); + DVEC3_NEAR(offset, expected_offset841, "Offset wrong", 1e-5); + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 0}, xhigh); + constexpr dvec3 expected_offset41 = {3, -3, 0}; + EXPECT_EQ(is_adjacent, true) << "ab"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset41, "Offset wrong", 1e-5); + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 1}, xhigh); + constexpr dvec3 expected_offset42 = {3, -3, 0}; + EXPECT_EQ(is_adjacent, true) << "ac"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 1}); + DVEC3_NEAR(offset, expected_offset42, "Offset wrong", 1e-5); + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 2}, xhigh); + constexpr dvec3 expected_offset43 = {3, -3, 0}; + EXPECT_EQ(is_adjacent, true) << "ad"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 2}); + DVEC3_NEAR(offset, expected_offset43, "Offset wrong", 1e-5); + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 3}, xhigh); + constexpr dvec3 expected_offset843 = {3, -3, 3}; + EXPECT_EQ(is_adjacent, true) << "ae"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset843, "Offset wrong", 1e-5); + + // on y + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ea"; + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 3}, yhigh); + constexpr dvec3 expected_offset00011 = {0, 3, 3}; + EXPECT_EQ(is_adjacent, true) << "eb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset00011, "Offset wrong", 1e-5); + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 3}, yhigh); + constexpr dvec3 expected_offset011 = {0, 3, 3}; + EXPECT_EQ(is_adjacent, true) << "ec"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 0}); + DVEC3_NEAR(offset, expected_offset011, "Offset wrong", 1e-5); + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 3}, yhigh); + constexpr dvec3 expected_offset0011 = {0, 3, 3}; + EXPECT_EQ(is_adjacent, true) << "ed"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 0}); + DVEC3_NEAR(offset, expected_offset0011, "Offset wrong", 1e-5); + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ee"; + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 2}, yhigh); + EXPECT_EQ(is_adjacent, false) << "da"; + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 2}, yhigh); + constexpr dvec3 expected_offset911 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset911, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 2}, yhigh); + constexpr dvec3 expected_offset912 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 2}); + DVEC3_NEAR(offset, expected_offset912, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 2}, yhigh); + constexpr dvec3 expected_offset913 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 2}); + DVEC3_NEAR(offset, expected_offset913, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 2}, yhigh); + EXPECT_EQ(is_adjacent, false) << "de"; + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ca"; + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 1}, yhigh); + constexpr dvec3 expected_offset921 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 1}); + DVEC3_NEAR(offset, expected_offset921, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 1}, yhigh); + constexpr dvec3 expected_offset922 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 1}); + DVEC3_NEAR(offset, expected_offset922, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 1}, yhigh); + constexpr dvec3 expected_offset923 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 1}); + DVEC3_NEAR(offset, expected_offset923, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ce"; + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, 0}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ba"; + + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 0}, yhigh); + constexpr dvec3 expected_offset931 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset931, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 0}, yhigh); + constexpr dvec3 expected_offset932 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 0}); + DVEC3_NEAR(offset, expected_offset932, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 0}, yhigh); + constexpr dvec3 expected_offset933 = {0, 3, 0}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 0}); + DVEC3_NEAR(offset, expected_offset933, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 0}, yhigh); + EXPECT_EQ(is_adjacent, false) << "be"; + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "aa"; + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, -1}, yhigh); + constexpr dvec3 expected_offset110000 = {0, 3, -3}; + EXPECT_EQ(is_adjacent, true) << "ab"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 2}); + DVEC3_NEAR(offset, expected_offset110000, "Offset wrong", 1e-5); + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, -1}, yhigh); + constexpr dvec3 expected_offset11200 = {0, 3, -3}; + EXPECT_EQ(is_adjacent, true) << "ac"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 2}); + DVEC3_NEAR(offset, expected_offset11200, "Offset wrong", 1e-5); + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, -1}, yhigh); + constexpr dvec3 expected_offset111000 = {0, 3, -3}; + EXPECT_EQ(is_adjacent, true) << "ad"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 2}); + DVEC3_NEAR(offset, expected_offset111000, "Offset wrong", 1e-5); + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, -1}, yhigh); + EXPECT_EQ(is_adjacent, false) << "ae"; + + // --------------------------------------------------------------------------- + // on z + // row 3 + // ea + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ea"; + + // eb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "eb"; + + // ec + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ec"; + + // ed + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ed"; + + // ee + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 3, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ee"; + + //----------------------------------------// + // row2 + // da + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "da"; + + // db + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 2, 3}, zhigh); + constexpr dvec3 expected_offset111 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "db"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 2, 0}); + DVEC3_NEAR(offset, expected_offset111, "Offset wrong", 1e-5); + + // dc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 2, 3}, zhigh); + constexpr dvec3 expected_offset112 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "dc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 2, 0}); + DVEC3_NEAR(offset, expected_offset112, "Offset wrong", 1e-5); + + // dd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 2, 3}, zhigh); + constexpr dvec3 expected_offset113 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "dd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 2, 0}); + DVEC3_NEAR(offset, expected_offset113, "Offset wrong", 1e-5); + + // de + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 2, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "de"; + + //----------------------------------------// + // row1 + // ca + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ca"; + + // cb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 1, 3}, zhigh); + constexpr dvec3 expected_offset121 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 1, 0}); + DVEC3_NEAR(offset, expected_offset121, "Offset wrong", 1e-5); + + // cc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 1, 3}, zhigh); + constexpr dvec3 expected_offset122 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 1, 0}); + DVEC3_NEAR(offset, expected_offset122, "Offset wrong", 1e-5); + + // cd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 1, 3}, zhigh); + constexpr dvec3 expected_offset123 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "cd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 1, 0}); + DVEC3_NEAR(offset, expected_offset123, "Offset wrong", 1e-5); + + // ce + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ce"; + + //----------------------------------------// + // row0 + // ba + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, 0, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ba"; + + // bb + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, 0, 3}, zhigh); + constexpr dvec3 expected_offset131 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bb"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(offset, expected_offset131, "Offset wrong", 1e-5); + + // bc + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, 0, 3}, zhigh); + constexpr dvec3 expected_offset132 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bc"; + EXPECT_IVEC3_EQ(new_coordinates, {1, 0, 0}); + DVEC3_NEAR(offset, expected_offset132, "Offset wrong", 1e-5); + + // bd + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, 0, 3}, zhigh); + constexpr dvec3 expected_offset133 = {0, 0, 3}; + EXPECT_EQ(is_adjacent, true) << "bd"; + EXPECT_IVEC3_EQ(new_coordinates, {2, 0, 0}); + DVEC3_NEAR(offset, expected_offset133, "Offset wrong", 1e-5); + + // be + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, 0, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "be"; + + //----------------------------------------// + // row-1 + // aa + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({-1, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "aa"; + + // ab + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({0, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ab"; + + // ac + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({1, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ac"; + + // ad + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({2, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ad"; + + // ae + std::tie(is_adjacent, new_coordinates, offset) = + container.reflectiveWarpAroundTesting({3, -1, 3}, zhigh); + EXPECT_EQ(is_adjacent, false) << "ae"; +} \ No newline at end of file diff --git a/tests/PeriodicBoundaryTest.cpp b/tests/PeriodicBoundaryTest.cpp index 26598eb0..d062f726 100644 --- a/tests/PeriodicBoundaryTest.cpp +++ b/tests/PeriodicBoundaryTest.cpp @@ -73,7 +73,7 @@ TEST(PeriodicBoundary, warpingBothPeriodicForX) { // ee std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ee, xhigh); + container.reflectiveWarpAroundTesting(ee, xhigh); constexpr dvec3 expected_offset1 = {9, 9, 0}; EXPECT_EQ(is_adjacent, true); EXPECT_IVEC3_EQ(new_coordinates, bb); @@ -81,7 +81,7 @@ TEST(PeriodicBoundary, warpingBothPeriodicForX) { // de std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(de, xhigh); + container.reflectiveWarpAroundTesting(de, xhigh); constexpr dvec3 expected_offset2 = {9, 0, 0}; EXPECT_EQ(is_adjacent, true); EXPECT_IVEC3_EQ(new_coordinates, db); @@ -89,7 +89,7 @@ TEST(PeriodicBoundary, warpingBothPeriodicForX) { // ce std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ce, xhigh); + container.reflectiveWarpAroundTesting(ce, xhigh); constexpr dvec3 expected_offset3 = {9, 0, 0}; EXPECT_EQ(is_adjacent, true); EXPECT_IVEC3_EQ(new_coordinates, cb); @@ -97,7 +97,7 @@ TEST(PeriodicBoundary, warpingBothPeriodicForX) { // be std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(be, xhigh); + container.reflectiveWarpAroundTesting(be, xhigh); constexpr dvec3 expected_offset4 = {9, 0, 0}; EXPECT_EQ(is_adjacent, true); EXPECT_IVEC3_EQ(new_coordinates, bb); @@ -105,7 +105,7 @@ TEST(PeriodicBoundary, warpingBothPeriodicForX) { // ae std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ae, xhigh); + container.reflectiveWarpAroundTesting(ae, xhigh); constexpr dvec3 expected_offset5 = {9, -9, 0}; EXPECT_EQ(is_adjacent, true); EXPECT_IVEC3_EQ(new_coordinates, db); @@ -134,12 +134,12 @@ TEST(PeriodicBoundary, warpingBothPeriodicForY) { // ea std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ea, yhigh); + container.reflectiveWarpAroundTesting(ea, yhigh); EXPECT_EQ(is_adjacent, false) << "wrong adjacency1"; // eb std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(eb, yhigh); + container.reflectiveWarpAroundTesting(eb, yhigh); constexpr dvec3 expected_offset2 = {0, 9, 0}; EXPECT_EQ(is_adjacent, true) << "wrong adjacency2"; EXPECT_IVEC3_EQ(new_coordinates, bb); @@ -147,7 +147,7 @@ TEST(PeriodicBoundary, warpingBothPeriodicForY) { // ec std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ec, yhigh); + container.reflectiveWarpAroundTesting(ec, yhigh); constexpr dvec3 expected_offset3 = {0, 9, 0}; EXPECT_EQ(is_adjacent, true) << "wrong adjacency3"; EXPECT_IVEC3_EQ(new_coordinates, bc); @@ -155,7 +155,7 @@ TEST(PeriodicBoundary, warpingBothPeriodicForY) { // ed std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ed, yhigh); + container.reflectiveWarpAroundTesting(ed, yhigh); constexpr dvec3 expected_offset4 = {0, 9, 0}; EXPECT_EQ(is_adjacent, true) << "wrong adjacency4"; EXPECT_IVEC3_EQ(new_coordinates, bd); @@ -163,7 +163,7 @@ TEST(PeriodicBoundary, warpingBothPeriodicForY) { // ee std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ee, yhigh); + container.reflectiveWarpAroundTesting(ee, yhigh); EXPECT_EQ(is_adjacent, false) << "wrong adjacency5"; } @@ -189,12 +189,12 @@ TEST(PeriodicBoundary, warpingXPeriodicForX) { // ee std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ee, xhigh); + container.reflectiveWarpAroundTesting(ee, xhigh); EXPECT_EQ(is_adjacent, false); // de std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(de, xhigh); + container.reflectiveWarpAroundTesting(de, xhigh); constexpr dvec3 expected_offset2 = {9, 0, 0}; EXPECT_EQ(is_adjacent, true); EXPECT_IVEC3_EQ(new_coordinates, db); @@ -202,7 +202,7 @@ TEST(PeriodicBoundary, warpingXPeriodicForX) { // ce std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ce, xhigh); + container.reflectiveWarpAroundTesting(ce, xhigh); constexpr dvec3 expected_offset3 = {9, 0, 0}; EXPECT_EQ(is_adjacent, true); EXPECT_IVEC3_EQ(new_coordinates, cb); @@ -210,7 +210,7 @@ TEST(PeriodicBoundary, warpingXPeriodicForX) { // be std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(be, xhigh); + container.reflectiveWarpAroundTesting(be, xhigh); constexpr dvec3 expected_offset4 = {9, 0, 0}; EXPECT_EQ(is_adjacent, true); EXPECT_IVEC3_EQ(new_coordinates, bb); @@ -218,7 +218,7 @@ TEST(PeriodicBoundary, warpingXPeriodicForX) { // ae std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ae, xhigh); + container.reflectiveWarpAroundTesting(ae, xhigh); EXPECT_EQ(is_adjacent, false); } @@ -244,12 +244,12 @@ TEST(PeriodicBoundary, warpingYPeriodicForY) { // ea std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ea, yhigh); + container.reflectiveWarpAroundTesting(ea, yhigh); EXPECT_EQ(is_adjacent, false) << "wrong adjacency1"; // eb std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(eb, yhigh); + container.reflectiveWarpAroundTesting(eb, yhigh); constexpr dvec3 expected_offset2 = {0, 9, 0}; EXPECT_EQ(is_adjacent, true) << "wrong adjacency2"; EXPECT_IVEC3_EQ(new_coordinates, bb); @@ -257,7 +257,7 @@ TEST(PeriodicBoundary, warpingYPeriodicForY) { // ec std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ec, yhigh); + container.reflectiveWarpAroundTesting(ec, yhigh); constexpr dvec3 expected_offset3 = {0, 9, 0}; EXPECT_EQ(is_adjacent, true) << "wrong adjacency3"; EXPECT_IVEC3_EQ(new_coordinates, bc); @@ -265,7 +265,7 @@ TEST(PeriodicBoundary, warpingYPeriodicForY) { // ed std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ed, yhigh); + container.reflectiveWarpAroundTesting(ed, yhigh); constexpr dvec3 expected_offset4 = {0, 9, 0}; EXPECT_EQ(is_adjacent, true) << "wrong adjacency4"; EXPECT_IVEC3_EQ(new_coordinates, bd); @@ -273,7 +273,7 @@ TEST(PeriodicBoundary, warpingYPeriodicForY) { // ee std::tie(is_adjacent, new_coordinates, offset) = - container.reflective_warp_around_testing(ee, yhigh); + container.reflectiveWarpAroundTesting(ee, yhigh); EXPECT_EQ(is_adjacent, false) << "wrong adjacency5"; } @@ -304,7 +304,7 @@ TEST(PeriodicBoundaryForce, offsetX) { const auto test = new Particle({-1, 1, 1}, {0, 0, 0}, 1, 5.0, 1); auto [is_adjacent, new_coordinates, particle_offset] = - container.reflective_warp_around_testing(be, xhigh); + container.reflectiveWarpAroundTesting(be, xhigh); constexpr dvec3 expected_offset = {9, 0, 0}; EXPECT_EQ(is_adjacent, true) << "wrong adjacency"; EXPECT_IVEC3_EQ(new_coordinates, bb); @@ -343,7 +343,7 @@ TEST(PeriodicBoundaryForce, offsetY) { const auto test = new Particle({1, -1, 1}, {0, 0, 0}, 1, 5.0, 1); auto [is_adjacent, new_coordinates, particle_offset] = - container.reflective_warp_around_testing(eb, yhigh); + container.reflectiveWarpAroundTesting(eb, yhigh); constexpr dvec3 expected_offset = {0, 9, 0}; EXPECT_EQ(is_adjacent, true) << "wrong adjacency"; EXPECT_IVEC3_EQ(new_coordinates, bb); @@ -357,6 +357,50 @@ TEST(PeriodicBoundaryForce, offsetY) { f.directionalForce(*test, *q)); } +/** + * tests that the correct force ist calculated for a periodic boundary in the z + * dimension + */ +TEST(PeriodicBoundaryForce, offsetZ) { + const LinkedCellsContainer container( + {.domain = {9, 9, 9}, + .cutoff_radius = 3, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + }}); + + const LennardJones f{}; + + // distance of 2, test particle is in theoretical position + const auto p = new Particle({1, 1, 8}, {0, 0, 0}, 1, 5.0, 1); // in bd + const auto q = new Particle({1, 1, 1}, {0, 0, 0}, 1, 5.0, 1); // in bb + const auto test = new Particle({1, 1, -1}, {0, 0, 0}, 1, 5.0, 1); + + auto [is_adjacent, new_coordinates, particle_offset] = + container.reflectiveWarpAroundTesting({0, 0, 3}, zhigh); + constexpr dvec3 expected_offset = {0, 0, 9}; + + SpdWrapper::get()->info("{}, [{}, {}, {}], [{}, {}, {}]", is_adjacent, + new_coordinates[0], new_coordinates[1], + new_coordinates[2], particle_offset[0], + particle_offset[1], particle_offset[2]); + EXPECT_EQ(is_adjacent, true) << "wrong adjacency"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(particle_offset, expected_offset, "Offset wrong", 1e-5); + + // compare that the force with offset is equal to what is the force in theory + const dvec3 accounted_particle_distance = + q->getX() - p->getX() + particle_offset; + ASSERT_EQ(LennardJones::directionalForceWithOffset( + *p, *q, accounted_particle_distance), + f.directionalForce(*test, *q)); +} + /** * tests that the correct force ist calculated for a periodic boundary in the x * and y dimension @@ -382,7 +426,7 @@ TEST(PeriodicBoundaryForce, offsetXY1) { const auto test = new Particle({-1, -1, 1}, {0, 0, 0}, 1, 5.0, 1); auto [is_adjacent, new_coordinates, particle_offset] = - container.reflective_warp_around_testing( + container.reflectiveWarpAroundTesting( ee, xhigh); // xhigh and not yhigh because corners are invalid for yhigh // if both boundaries are periodic to avoid double matching @@ -394,9 +438,10 @@ TEST(PeriodicBoundaryForce, offsetXY1) { // compare that the force with offset is equal to what is the force in theory const dvec3 accounted_particle_distance = q->getX() - p->getX() + particle_offset; - ASSERT_EQ(LennardJones::directionalForceWithOffset( - *p, *q, accounted_particle_distance), - f.directionalForce(*test, *q)); + DVEC3_NEAR(LennardJones::directionalForceWithOffset( + *p, *q, accounted_particle_distance), + f.directionalForce(*test, *q), "not equal to theoretical force", + 1e-8); } /** @@ -424,7 +469,7 @@ TEST(PeriodicBoundaryForce, offsetXY2) { const auto test = new Particle({-1, 10, 1}, {0, 0, 0}, 1, 5.0, 1); auto [is_adjacent, new_coordinates, particle_offset] = - container.reflective_warp_around_testing( + container.reflectiveWarpAroundTesting( ae, xhigh); // xhigh and not yhigh because corners are invalid for yhigh // if both boundaries are periodic to avoid double matching @@ -436,9 +481,61 @@ TEST(PeriodicBoundaryForce, offsetXY2) { // compare that the force with offset is equal to what is the force in theory const dvec3 accounted_particle_distance = q->getX() - p->getX() + particle_offset; - ASSERT_EQ(LennardJones::directionalForceWithOffset( - *p, *q, accounted_particle_distance), - f.directionalForce(*test, *q)); + DVEC3_NEAR(LennardJones::directionalForceWithOffset( + *p, *q, accounted_particle_distance), + f.directionalForce(*test, *q), "not equal to theoretical force", + 1e-8); +} + +/** + * note that the offset tests do not add any real value over the warp tests, + * therefore, for the extension to 3D, these will include fewer examples for Z. + * It only has to be tested that the directionalForceWithOffset accepts a Z + * offset + */ + +/** + * tests that the correct force ist calculated for a periodic boundary in the x, + * y and z dimension + */ +TEST(PeriodicBoundaryForce, offsetXYZ) { + const LinkedCellsContainer container( + {.domain = {9, 9, 9}, + .cutoff_radius = 3, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + }}); + + const LennardJones f{}; + + // distance of 2, test particle is in theoretical position + const auto p = new Particle({8, 8, 8}, {0, 0, 0}, 1, 5.0, 1); // in bd + const auto q = new Particle({1, 1, 1}, {0, 0, 0}, 1, 5.0, 1); // in bb + const auto test = new Particle({-1, -1, -1}, {0, 0, 0}, 1, 5.0, 1); + + auto [is_adjacent, new_coordinates, particle_offset] = + container.reflectiveWarpAroundTesting( + {3, 3, 3}, + xhigh); // xhigh and not yhigh or zhigh because corners are invalid + // for yhigh and zhigh + // if all three boundaries are periodic to avoid double matching + constexpr dvec3 expected_offset = {9, 9, 9}; + EXPECT_EQ(is_adjacent, true) << "wrong adjacency"; + EXPECT_IVEC3_EQ(new_coordinates, {0, 0, 0}); + DVEC3_NEAR(particle_offset, expected_offset, "Offset wrong", 1e-5); + + // compare that the force with offset is equal to what is the force in theory + const dvec3 accounted_particle_distance = + q->getX() - p->getX() + particle_offset; + DVEC3_NEAR(LennardJones::directionalForceWithOffset( + *p, *q, accounted_particle_distance), + f.directionalForce(*test, *q), "not equal to theoretical force", + 1e-8); } //[[======================== moving test =====================================]] @@ -461,9 +558,9 @@ TEST(PeriodicBoundaryMoving, moveXLeft) { const LennardJones f{}; // move to other end - const Particle one({1, 1, 1}, {-2, 0, 0}, 1, 5.0, 1); // in bb + Particle one({1, 1, 1}, {-2, 0, 0}, 1, 5.0, 1); // in bb - container.addParticle(one); + container.addParticles({one}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 2"; double delta_t = 1; @@ -490,9 +587,9 @@ TEST(PeriodicBoundaryMoving, moveXLeft) { // test that particle is registered in its true cell const auto old_cell = - container.getCells()[container.cellCoordToIndex_testing({0, 0, 0})]; + container.getCells()[container.cellCoordToIndexTesting({0, 0, 0})]; const auto new_cell = - container.getCells()[container.cellCoordToIndex_testing({2, 0, 0})]; + container.getCells()[container.cellCoordToIndexTesting({2, 0, 0})]; EXPECT_EQ(old_cell.size(), 0); EXPECT_EQ(new_cell.size(), 1); @@ -516,9 +613,10 @@ TEST(PeriodicBoundaryMoving, moveXRight) { const LennardJones f{}; // move to other end - const Particle one({8, 1, 1}, {2, 0, 0}, 1, 5.0, 1); // in bb - container.addParticle(one); + Particle one({8, 1, 1}, {2, 0, 0}, 1, 5.0, 1); // in bb + + container.addParticles({one}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 2"; double delta_t = 1; @@ -545,9 +643,9 @@ TEST(PeriodicBoundaryMoving, moveXRight) { // test that particle is registered in its true cell const auto old_cell = - container.getCells()[container.cellCoordToIndex_testing({2, 0, 0})]; + container.getCells()[container.cellCoordToIndexTesting({2, 0, 0})]; const auto new_cell = - container.getCells()[container.cellCoordToIndex_testing({0, 0, 0})]; + container.getCells()[container.cellCoordToIndexTesting({0, 0, 0})]; EXPECT_EQ(old_cell.size(), 0); EXPECT_EQ(new_cell.size(), 1); @@ -557,7 +655,7 @@ TEST(PeriodicBoundaryMoving, moveXRight) { * tests that particles can move through the x periodic boundary */ TEST(PeriodicBoundaryMoving, moveXDiagonal1) { - LinkedCellsContainer container({.domain = {9, 9, 3}, + LinkedCellsContainer container({.domain = {9, 9, 9}, .cutoff_radius = 3, .boundary_config = { LinkedCellsConfig::BoundaryType::Periodic, @@ -571,32 +669,39 @@ TEST(PeriodicBoundaryMoving, moveXDiagonal1) { const LennardJones f{}; // move to other end - const Particle one({8, 8, 1}, {2, 2, 0}, 1, 5.0, 1); - const Particle two({8, 1, 1}, {2, -2, 0}, 1, 5.0, 1); + Particle one({8, 8, 1}, {2, 2, 0}, 1, 5.0, 1); + Particle two({8, 1, 1}, {2, -2, 0}, 1, 5.0, 1); - container.addParticle(one); - container.addParticle(two); + std::cout << "Adding Particles" << std::endl; + container.addParticles({one, two}); EXPECT_EQ(container.size(), 2) << "Number of Particles is not 2"; + SpdWrapper::get()->info("Starting test"); + std::cout << "Starting Test" << std::endl; + double delta_t = 1; container.singleIterator([this, delta_t](Particle& p) { const dvec3 new_x = p.getX() + delta_t * p.getV() + (delta_t * delta_t / (2 * p.getM())) * (p.getF()); p.setX(new_x); + std::cout << "setting p " << p.getId() << " new_x [" << new_x[0] << "," + << new_x[1] << "," << new_x[2] << "]" << std::endl; }); - container.singleIterator([](Particle& p) { p.updateForceInTime(); }); + SpdWrapper::get()->info("Positions Updated"); + std::cout << "Positions Updated" << std::endl; container.imposeInvariant(); - - container.singleIterator([this, delta_t](Particle& p) { - const dvec3 new_v = - p.getV() + (delta_t / (2 * p.getM()) * (p.getOldF() + p.getF())); - p.setV(new_v); - }); - - container.singleIterator( - [this](Particle& p) { FAIL() << "Particle should have been deleted"; }); + SpdWrapper::get()->info("Invariant Imposed"); + std::cout << "Invariant Imposed" << std::endl; + + for (const auto& c : container.getCells()) { + for (const auto p : c) { + SpdWrapper::get()->info("p {} is at [{}, {}, {}]", p->getId(), + p->getX()[0], p->getX()[1], p->getX()[2]); + FAIL() << "Particle should have been deleted"; + } + } } /** @@ -617,11 +722,10 @@ TEST(PeriodicBoundaryMoving, moveXDiagonal2) { const LennardJones f{}; // move to other end - const Particle one({1, 1, 1}, {-2, -2, 0}, 1, 5.0, 1); - const Particle two({1, 8, 1}, {-2, 2, 0}, 1, 5.0, 1); + Particle one({1, 1, 1}, {-2, -2, 0}, 1, 5.0, 1); + Particle two({1, 8, 1}, {-2, 2, 0}, 1, 5.0, 1); - container.addParticle(one); - container.addParticle(two); + container.addParticles({one, two}); EXPECT_EQ(container.size(), 2) << "Number of Particles is not 2"; double delta_t = 1; @@ -641,8 +745,13 @@ TEST(PeriodicBoundaryMoving, moveXDiagonal2) { p.setV(new_v); }); - container.singleIterator( - [this](Particle& p) { FAIL() << "Particle should have been deleted"; }); + for (const auto& c : container.getCells()) { + for (const auto p : c) { + SpdWrapper::get()->info("p {} is at [{}, {}, {}]", p->getId(), + p->getX()[0], p->getX()[1], p->getX()[2]); + FAIL() << "Particle should have been deleted"; + } + } } /** @@ -663,11 +772,10 @@ TEST(PeriodicBoundaryMoving, moveYDiagonal1) { const LennardJones f{}; // move to other end - const Particle one({8, 8, 1}, {2, 2, 0}, 1, 5.0, 1); - const Particle two({8, 1, 1}, {2, -2, 0}, 1, 5.0, 1); + Particle one({8, 8, 1}, {2, 2, 0}, 1, 5.0, 1); + Particle two({8, 1, 1}, {2, -2, 0}, 1, 5.0, 1); - container.addParticle(one); - container.addParticle(two); + container.addParticles({one, two}); EXPECT_EQ(container.size(), 2) << "Number of Particles is not 2"; double delta_t = 1; @@ -687,8 +795,13 @@ TEST(PeriodicBoundaryMoving, moveYDiagonal1) { p.setV(new_v); }); - container.singleIterator( - [this](Particle& p) { FAIL() << "Particle should have been deleted"; }); + for (const auto& c : container.getCells()) { + for (const auto p : c) { + SpdWrapper::get()->info("p {} is at [{}, {}, {}]", p->getId(), + p->getX()[0], p->getX()[1], p->getX()[2]); + FAIL() << "Particle should have been deleted"; + } + } } /** @@ -709,11 +822,10 @@ TEST(PeriodicBoundaryMoving, moveYDiagonal2) { const LennardJones f{}; // move to other end - const Particle one({1, 1, 1}, {-2, -2, 0}, 1, 5.0, 1); - const Particle two({1, 8, 1}, {-2, 2, 0}, 1, 5.0, 1); + Particle one({1, 1, 1}, {-2, -2, 0}, 1, 5.0, 1); + Particle two({1, 8, 1}, {-2, 2, 0}, 1, 5.0, 1); - container.addParticle(one); - container.addParticle(two); + container.addParticles({one, two}); EXPECT_EQ(container.size(), 2) << "Number of Particles is not 2"; double delta_t = 1; @@ -733,8 +845,13 @@ TEST(PeriodicBoundaryMoving, moveYDiagonal2) { p.setV(new_v); }); - container.singleIterator( - [this](Particle& p) { FAIL() << "Particle should have been deleted"; }); + for (const auto& c : container.getCells()) { + for (const auto p : c) { + SpdWrapper::get()->info("p {} is at [{}, {}, {}]", p->getId(), + p->getX()[0], p->getX()[1], p->getX()[2]); + FAIL() << "Particle should have been deleted"; + } + } } /** @@ -755,9 +872,9 @@ TEST(PeriodicBoundaryMoving, moveYDown) { const LennardJones f{}; // move to other end - const Particle one({1, 1, 1}, {0, -2, 0}, 1, 5.0, 1); // in bb + Particle one({1, 1, 1}, {0, -2, 0}, 1, 5.0, 1); // in bb - container.addParticle(one); + container.addParticles({one}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 2"; double delta_t = 1; @@ -784,9 +901,9 @@ TEST(PeriodicBoundaryMoving, moveYDown) { // test that particle is registered in its true cell const auto old_cell = - container.getCells()[container.cellCoordToIndex_testing({0, 0, 0})]; + container.getCells()[container.cellCoordToIndexTesting({0, 0, 0})]; const auto new_cell = - container.getCells()[container.cellCoordToIndex_testing({0, 2, 0})]; + container.getCells()[container.cellCoordToIndexTesting({0, 2, 0})]; EXPECT_EQ(old_cell.size(), 0); EXPECT_EQ(new_cell.size(), 1); @@ -810,9 +927,9 @@ TEST(PeriodicBoundaryMoving, moveYUp) { const LennardJones f{}; // move to other end - const Particle one({1, 8, 1}, {0, 2, 0}, 1, 5.0, 1); // in bb + Particle one({1, 8, 1}, {0, 2, 0}, 1, 5.0, 1); // in bb - container.addParticle(one); + container.addParticles({one}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 2"; double delta_t = 1; @@ -839,9 +956,9 @@ TEST(PeriodicBoundaryMoving, moveYUp) { // test that particle is registered in its true cell const auto old_cell = - container.getCells()[container.cellCoordToIndex_testing({0, 2, 0})]; + container.getCells()[container.cellCoordToIndexTesting({0, 2, 0})]; const auto new_cell = - container.getCells()[container.cellCoordToIndex_testing({0, 0, 0})]; + container.getCells()[container.cellCoordToIndexTesting({0, 0, 0})]; EXPECT_EQ(old_cell.size(), 0); EXPECT_EQ(new_cell.size(), 1); @@ -865,9 +982,9 @@ TEST(PeriodicBoundaryMoving, moveXYDiagonal1) { const LennardJones f{}; // move to other end - const Particle one({1, 1, 1}, {-2, -2, 0}, 1, 5.0, 1); + Particle one({1, 1, 1}, {-2, -2, 0}, 1, 5.0, 1); - container.addParticle(one); + container.addParticles({one}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 1"; double delta_t = 1; @@ -894,9 +1011,9 @@ TEST(PeriodicBoundaryMoving, moveXYDiagonal1) { // test that particle is registered in its true cell const auto old_cell = - container.getCells()[container.cellCoordToIndex_testing({0, 0, 0})]; + container.getCells()[container.cellCoordToIndexTesting({0, 0, 0})]; const auto new_cell = - container.getCells()[container.cellCoordToIndex_testing({2, 2, 0})]; + container.getCells()[container.cellCoordToIndexTesting({2, 2, 0})]; EXPECT_EQ(old_cell.size(), 0); EXPECT_EQ(new_cell.size(), 1); @@ -920,9 +1037,9 @@ TEST(PeriodicBoundaryMoving, moveXYDiagonal2) { const LennardJones f{}; // move to other end - const Particle one({8, 1, 1}, {2, -2, 0}, 1, 5.0, 1); + Particle one({8, 1, 1}, {2, -2, 0}, 1, 5.0, 1); - container.addParticle(one); + container.addParticles({one}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 2"; double delta_t = 1; @@ -949,9 +1066,9 @@ TEST(PeriodicBoundaryMoving, moveXYDiagonal2) { // test that particle is registered in its true cell const auto old_cell = - container.getCells()[container.cellCoordToIndex_testing({2, 0, 0})]; + container.getCells()[container.cellCoordToIndexTesting({2, 0, 0})]; const auto new_cell = - container.getCells()[container.cellCoordToIndex_testing({0, 2, 0})]; + container.getCells()[container.cellCoordToIndexTesting({0, 2, 0})]; EXPECT_EQ(old_cell.size(), 0); EXPECT_EQ(new_cell.size(), 1); @@ -975,9 +1092,9 @@ TEST(PeriodicBoundaryMoving, moveXYDiagonal3) { const LennardJones f{}; // move to other end - const Particle one({1, 8, 1}, {-2, 2, 0}, 1, 5.0, 1); + Particle one({1, 8, 1}, {-2, 2, 0}, 1, 5.0, 1); - container.addParticle(one); + container.addParticles({one}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 1"; double delta_t = 1; @@ -1004,9 +1121,9 @@ TEST(PeriodicBoundaryMoving, moveXYDiagonal3) { // test that particle is registered in its true cell const auto old_cell = - container.getCells()[container.cellCoordToIndex_testing({0, 2, 0})]; + container.getCells()[container.cellCoordToIndexTesting({0, 2, 0})]; const auto new_cell = - container.getCells()[container.cellCoordToIndex_testing({2, 0, 0})]; + container.getCells()[container.cellCoordToIndexTesting({2, 0, 0})]; EXPECT_EQ(old_cell.size(), 0); EXPECT_EQ(new_cell.size(), 1); @@ -1030,9 +1147,9 @@ TEST(PeriodicBoundaryMoving, moveXYDiagonal4) { const LennardJones f{}; // move to other end - const Particle one({8, 8, 1}, {2, 2, 0}, 1, 5.0, 1); + Particle one({8, 8, 1}, {2, 2, 0}, 1, 5.0, 1); - container.addParticle(one); + container.addParticles({one}); EXPECT_EQ(container.size(), 1) << "Number of Particles is not 2"; double delta_t = 1; @@ -1059,10 +1176,175 @@ TEST(PeriodicBoundaryMoving, moveXYDiagonal4) { // test that particle is registered in its true cell const auto old_cell = - container.getCells()[container.cellCoordToIndex_testing({2, 2, 0})]; + container.getCells()[container.cellCoordToIndexTesting({2, 2, 0})]; const auto new_cell = - container.getCells()[container.cellCoordToIndex_testing({0, 0, 0})]; + container.getCells()[container.cellCoordToIndexTesting({0, 0, 0})]; EXPECT_EQ(old_cell.size(), 0); EXPECT_EQ(new_cell.size(), 1); } + +/** + * tests that particles can move through the y periodic boundary + */ +TEST(PeriodicBoundaryMoving, moveZForward) { + LinkedCellsContainer container({.domain = {9, 9, 9}, + .cutoff_radius = 3, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + }}); + + const LennardJones f{}; + + // move to other end + Particle one({1, 1, 1}, {0, 0, -2}, 1, 5.0, 1); // in bb + + container.addParticles({one}); + EXPECT_EQ(container.size(), 1) << "Number of Particles is not 2"; + + double delta_t = 1; + container.singleIterator([this, delta_t](Particle& p) { + const dvec3 new_x = p.getX() + delta_t * p.getV() + + (delta_t * delta_t / (2 * p.getM())) * (p.getF()); + p.setX(new_x); + }); + + container.singleIterator([](Particle& p) { p.updateForceInTime(); }); + + container.imposeInvariant(); + + container.singleIterator([this, delta_t](Particle& p) { + const dvec3 new_v = + p.getV() + (delta_t / (2 * p.getM()) * (p.getOldF() + p.getF())); + p.setV(new_v); + }); + + container.singleIterator([this](Particle& p) { + DVEC3_NEAR(p.getV(), {0.0, 0.0, -2.0}, "wrong velocity", 1e-5); + DVEC3_NEAR(p.getX(), {1, 1, 8}, "wrong position", 1e-5); + }); + + // test that particle is registered in its true cell + const auto old_cell = + container.getCells()[container.cellCoordToIndexTesting({0, 0, 0})]; + const auto new_cell = + container.getCells()[container.cellCoordToIndexTesting({0, 0, 2})]; + + EXPECT_EQ(old_cell.size(), 0); + EXPECT_EQ(new_cell.size(), 1); +} + +/** + * tests that particles can move through the z periodic boundary + */ +TEST(PeriodicBoundaryMoving, moveZBackward) { + LinkedCellsContainer container({.domain = {9, 9, 9}, + .cutoff_radius = 3, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + }}); + + const LennardJones f{}; + + // move to other end + Particle one({1, 1, 8}, {0, 0, 2}, 1, 5.0, 1); // in bb + + container.addParticles({one}); + EXPECT_EQ(container.size(), 1) << "Number of Particles is not 1"; + + double delta_t = 1; + container.singleIterator([this, delta_t](Particle& p) { + const dvec3 new_x = p.getX() + delta_t * p.getV() + + (delta_t * delta_t / (2 * p.getM())) * (p.getF()); + p.setX(new_x); + }); + + container.singleIterator([](Particle& p) { p.updateForceInTime(); }); + + container.imposeInvariant(); + + container.singleIterator([this, delta_t](Particle& p) { + const dvec3 new_v = + p.getV() + (delta_t / (2 * p.getM()) * (p.getOldF() + p.getF())); + p.setV(new_v); + }); + + container.singleIterator([this](Particle& p) { + DVEC3_NEAR(p.getV(), {0.0, 0.0, 2.0}, "wrong velocity", 1e-5); + DVEC3_NEAR(p.getX(), {1, 1, 1}, "wrong position", 1e-5); + }); + + // test that particle is registered in its true cell + const auto old_cell = + container.getCells()[container.cellCoordToIndexTesting({0, 0, 2})]; + const auto new_cell = + container.getCells()[container.cellCoordToIndexTesting({0, 0, 0})]; + + EXPECT_EQ(old_cell.size(), 0); + EXPECT_EQ(new_cell.size(), 1); +} + +/** + * tests that particles can move through the x, y and z periodic boundary + */ +TEST(PeriodicBoundaryMoving, moveXYZDiagonal) { + LinkedCellsContainer container({.domain = {9, 9, 9}, + .cutoff_radius = 3, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + LinkedCellsConfig::BoundaryType::Periodic, + }}); + + const LennardJones f{}; + + // move to other end + Particle one({8, 8, 8}, {2, 2, 2}, 1, 5.0, 1); // in bb + + container.addParticles({one}); + EXPECT_EQ(container.size(), 1) << "Number of Particles is not 1"; + + double delta_t = 1; + container.singleIterator([this, delta_t](Particle& p) { + const dvec3 new_x = p.getX() + delta_t * p.getV() + + (delta_t * delta_t / (2 * p.getM())) * (p.getF()); + p.setX(new_x); + }); + + container.singleIterator([](Particle& p) { p.updateForceInTime(); }); + + container.imposeInvariant(); + + container.singleIterator([this, delta_t](Particle& p) { + const dvec3 new_v = + p.getV() + (delta_t / (2 * p.getM()) * (p.getOldF() + p.getF())); + p.setV(new_v); + }); + + container.singleIterator([this](Particle& p) { + DVEC3_NEAR(p.getV(), {2.0, 2.0, 2.0}, "wrong velocity", 1e-5); + DVEC3_NEAR(p.getX(), {1, 1, 1}, "wrong position", 1e-5); + }); + + // test that particle is registered in its true cell + const auto old_cell = + container.getCells()[container.cellCoordToIndexTesting({2, 2, 2})]; + const auto new_cell = + container.getCells()[container.cellCoordToIndexTesting({0, 0, 0})]; + + EXPECT_EQ(old_cell.size(), 0); + EXPECT_EQ(new_cell.size(), 1); +} \ No newline at end of file diff --git a/tests/SingularGravityTest.cpp b/tests/SingularGravityTest.cpp new file mode 100644 index 00000000..20cc4ed3 --- /dev/null +++ b/tests/SingularGravityTest.cpp @@ -0,0 +1,52 @@ +// +// Created by jkr on 1/28/25. +// +#include + +#include + +#include "../src/forces/IndexForce.h" +#include "../src/utils/ArrayUtils.h" +#include "debug/debug_print.h" +#include "forces/SingularGravity.h" +#include "testUtil.h" + +/** + * Tests singular gravity correctness in x direction + */ +TEST(SingularGravity, x_direction) { + SingularGravity sg(-12.44, 0); + Particle p({0, 0, 0}, {0, 0, 0}, 1, 1, 1); + p.addF(sg.applyForce(p)); + DVEC3_NEAR(p.getF(), {-12.44, 0, 0}, "Singular gravity not equal"); +} + +/** + * Tests singular gravity correctness in y direction + */ +TEST(SingularGravity, y_direction) { + SingularGravity sg(-12.44, 1); + Particle p({0, 0, 0}, {0, 0, 0}, 1, 1, 1); + p.addF(sg.applyForce(p)); + DVEC3_NEAR(p.getF(), {0, -12.44, 0.0}, "Singular Gravity not equal"); +} + +/** + * Tests singular gravity correctness in z direction + */ +TEST(SingularGravity, z_direction) { + SingularGravity sg(-12.44, 2); + Particle p({0, 0, 0}, {0, 0, 0}, 1, 1, 1); + p.addF(sg.applyForce(p)); + DVEC3_NEAR(p.getF(), {0, 0, -12.44}, "Singular Gravity not equal"); +} + +/** + * Tests singular gravity correctness for different mass + */ +TEST(SingularGravity, mass_multiplier) { + SingularGravity sg(-12.44, 1); + Particle p({0, 0, 0}, {0, 0, 0}, 2, 1, 1); + p.addF(sg.applyForce(p)); + DVEC3_NEAR(p.getF(), {0, -24.88, 0.0}, "Singular Gravity not equal"); +} diff --git a/tests/StatisticsTest.cpp b/tests/StatisticsTest.cpp new file mode 100644 index 00000000..2761377f --- /dev/null +++ b/tests/StatisticsTest.cpp @@ -0,0 +1,153 @@ +// +// Created by maximilian on 22.01.25. +// +#include + +#include +#include +#include + +#include "defs/Particle.h" +#include "defs/Thermostat.h" +#include "defs/containers/LinkedCellsContainer.h" +#include "testUtil.h" +#include "utils/ArrayUtils.h" +#include "utils/Statistics.h" + +/** + * tests that wall particles are excluded from the thermostat + */ +TEST(Statistics, WallThermostat) { + LinkedCellsContainer container( + {.domain = {3, 3, 3}, // better safe than sorry + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + }}); + + constexpr ThermostatConfig config = { + .t_init = 0, + .t_target = 1, + .delta_t = 1, + .n_thermostat = 1, + .use_relative = false, + .use_thermal_motion = true, + }; + + Thermostat thermostat(config); + + Particle wall({2, 1, 1}, {1, 1, 1}, 1, 1, 1, -1); + Particle p({1, 2, 1}, {2, 2, 2}, 1, 1, 1, 1); + Particle q({1, 1, 2}, {3, 3, 3}, 1, 1, 1, 2); + container.addParticles({wall}); + container.addParticles({p}); + container.addParticles({q}); + + EXPECT_EQ(container.getParticleCount(), 3) << "Particle Count wrong"; + EXPECT_EQ(container.getSpecialParticleCount(), 1) << "Special Particle Count wrong"; + EXPECT_EQ(container.size(), 3) << "Number of Particles is not 0"; + + DVEC3_NEAR(Thermostat::getAverageVelocity(container), {2.5, 2.5, 2.5}, "average velocity wrong", 1e-5); +} + +/** + * tests that the thermal temperature ignores walls + */ +TEST(Statistics, ThermalTemperature) { + LinkedCellsContainer container( + {.domain = {3, 3, 3}, // better safe than sorry + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + }}); + + constexpr ThermostatConfig config = { + .t_init = 0, + .t_target = 1, + .delta_t = 1, + .n_thermostat = 1, + .use_relative = false, + .use_thermal_motion = true, + }; + + Thermostat thermostat(config); + + Particle wall({2, 1, 1}, {1, 1, 1}, 1, 1, 1, -1); + Particle p({1, 2, 1}, {2, 2, 2}, 1, 1, 1, 1); + Particle q({1, 1, 2}, {3, 3, 3}, 2, 1, 1, 2); + container.addParticles({wall}); + container.addParticles({p}); + container.addParticles({q}); + + EXPECT_EQ(container.getParticleCount(), 3) << "Particle Count wrong"; + EXPECT_EQ(container.getSpecialParticleCount(), 1) << "Special Particle Count wrong"; + EXPECT_EQ(container.size(), 3) << "Number of Particles is not 0"; + + dvec3 average_velocity = Thermostat::getAverageVelocity(container); + DVEC3_NEAR(average_velocity, {2.5, 2.5, 2.5}, "average velocity wrong", 1e-5); + + EXPECT_EQ(thermostat.getThermalTemperature(container, average_velocity), 0.375); +} + +/** + * tests that the statistics are correct + * tested with ybins = 1, because this is the official bin type + */ +TEST(Statistics, bins) { + LinkedCellsContainer container( + {.domain = {3, 3, 3}, // better safe than sorry + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + }}); + + std::filesystem::path tempDir = std::filesystem::temp_directory_path(); + std::string densityFile = (tempDir / "density.txt").string(); + std::string velocityFile = (tempDir / "velocity.txt").string(); + + Statistics statistics(3, 1, container, densityFile, velocityFile); + + Particle wall({0.5, 0.5, 0.5}, {1, 1, 1}, 1, 1, 1, -1); + Particle p({0.5, 0.5, 0.5}, {2, 2, 2}, 1, 1, 1, 1); + Particle q({0.5, 0.5, 0.5}, {3, 3, 1}, 2, 1, 1, 2); + Particle r({2.5, 2.5, 0.5}, {4, 3, 3}, 2, 1, 1, 2); + + container.addParticles({wall}); + container.addParticles({p}); + container.addParticles({q}); + container.addParticles({r}); + + EXPECT_EQ(container.getParticleCount(), 4) << "Particle Count wrong"; + + statistics.writeStatistics(1); + statistics.closeFiles(); + + std::ifstream dfile(densityFile); + std::ifstream vfile(velocityFile); + + if (!dfile || !vfile) { + FAIL() << "Could not open at least one of the files"; + } + + std::string line; + std::getline(dfile, line); + ASSERT_EQ(line, "1,0.222222,0.000000,0.111111"); + + std::getline(vfile, line); + ASSERT_EQ(line, "1,2.500000 2.500000 1.500000,0.0 0.0 0.0,4.000000 3.000000 3.000000"); +} \ No newline at end of file diff --git a/tests/ThermostatTest.cpp b/tests/ThermostatTest.cpp index f01c77f5..145593d9 100644 --- a/tests/ThermostatTest.cpp +++ b/tests/ThermostatTest.cpp @@ -19,7 +19,7 @@ TEST(Thermostat, holding) { char arg0[] = "MolSim"; char arg1[] = "-f"; - char arg2[] = "../../../tests/test_cuboid.xml"; + char arg2[] = "../../tests/test_cuboid.xml"; char* argv[] = {arg0, arg1, arg2}; auto [name, step, checkpoint] = CLArgumentParser::parse(3, argv); @@ -58,28 +58,29 @@ TEST(Thermostat, holding) { std::vector> singular_forces; for (auto config : arguments.singular_force_types) { if (std::holds_alternative(config)) { - const auto& [g] = std::get(config); - singular_forces.push_back( - std::move(std::make_unique(g))); + const auto& [g, a] = std::get(config); + singular_forces.push_back(std::make_unique(g, a)); } else { SpdWrapper::get()->error("Unrecognized singular force"); } } + std::vector> index_forces; + VerletIntegrator verlet_integrator(interactive_forces, singular_forces, - arguments.delta_t); + index_forces, arguments.delta_t); std::unique_ptr thermostat; if (arguments.use_thermostat) { thermostat = std::make_unique(arguments.thermostat_config); } - double cur_temp = Thermostat::getTemperature(*container); + double cur_temp = thermostat->getTemperature(*container); EXPECT_NEAR(cur_temp, 1, 1); for (std::size_t i = 0; i < 1000; i++) { verlet_integrator.step(*container); } - double temp = Thermostat::getTemperature(*container); + double temp = thermostat->getTemperature(*container); EXPECT_NE(temp, 0.0); thermostat->setTemperature(*container); @@ -87,10 +88,10 @@ TEST(Thermostat, holding) { EXPECT_NEAR(thermostat->getTemperature(*container), 1.0, 1e-6); for (std::size_t i = 0; i < 10000; i++) { - if (i != 0 && i % thermostat->n_thermostat) { + if (i != 0 && i % thermostat->getNThermostat()) { thermostat->setTemperature(*container); - EXPECT_NEAR(thermostat->getTemperature(*container), thermostat->T_target, - 1e-6); + EXPECT_NEAR(thermostat->getTemperature(*container), + thermostat->getTTarget(), 1e-6); } } } @@ -101,7 +102,7 @@ TEST(Thermostat, holding) { TEST(Thermostat, cooling) { char arg0[] = "MolSim"; char arg1[] = "-f"; - char arg2[] = "../../../tests/test_cuboid_cooling.xml"; + char arg2[] = "../../tests/test_cuboid_cooling.xml"; char* argv[] = {arg0, arg1, arg2}; auto [name, step, checkpoint] = CLArgumentParser::parse(3, argv); @@ -140,28 +141,28 @@ TEST(Thermostat, cooling) { std::vector> singular_forces; for (auto config : arguments.singular_force_types) { if (std::holds_alternative(config)) { - const auto& [g] = std::get(config); - singular_forces.push_back( - std::move(std::make_unique(g))); + const auto& [g, a] = std::get(config); + singular_forces.push_back(std::make_unique(g, a)); } else { SpdWrapper::get()->error("Unrecognized singular force"); } } + std::vector> index_forces; VerletIntegrator verlet_integrator(interactive_forces, singular_forces, - arguments.delta_t); + index_forces, arguments.delta_t); std::unique_ptr thermostat; if (arguments.use_thermostat) { thermostat = std::make_unique(arguments.thermostat_config); } - double cur_temp = Thermostat::getTemperature(*container); - EXPECT_NEAR(cur_temp, 22, 4); + double cur_temp = thermostat->getTemperature(*container); + EXPECT_NEAR(cur_temp, 22, 1); for (std::size_t i = 0; i < 1000; i++) { verlet_integrator.step(*container); } - double temp = Thermostat::getTemperature(*container); + double temp = thermostat->getTemperature(*container); EXPECT_NE(temp, 0.0); thermostat->setTemperature(*container); @@ -175,7 +176,7 @@ TEST(Thermostat, cooling) { TEST(Thermostat, heating) { char arg0[] = "MolSim"; char arg1[] = "-f"; - char arg2[] = "../../../tests/test_cuboid_heating.xml"; + char arg2[] = "../../tests/test_cuboid_heating.xml"; char* argv[] = {arg0, arg1, arg2}; auto [name, step, checkpoint] = CLArgumentParser::parse(3, argv); @@ -214,28 +215,29 @@ TEST(Thermostat, heating) { std::vector> singular_forces; for (auto config : arguments.singular_force_types) { if (std::holds_alternative(config)) { - const auto& [g] = std::get(config); - singular_forces.push_back( - std::move(std::make_unique(g))); + const auto& [g, a] = std::get(config); + singular_forces.push_back(std::make_unique(g, a)); } else { SpdWrapper::get()->error("Unrecognized singular force"); } } + std::vector> index_forces; + VerletIntegrator verlet_integrator(interactive_forces, singular_forces, - arguments.delta_t); + index_forces, arguments.delta_t); std::unique_ptr thermostat; if (arguments.use_thermostat) { thermostat = std::make_unique(arguments.thermostat_config); } - double cur_temp = Thermostat::getTemperature(*container); + double cur_temp = thermostat->getTemperature(*container); EXPECT_NEAR(cur_temp, 1.0, 1); for (std::size_t i = 0; i < 1000; i++) { verlet_integrator.step(*container); } - double temp = Thermostat::getTemperature(*container); + double temp = thermostat->getTemperature(*container); EXPECT_NE(temp, 0.0); thermostat->setTemperature(*container); @@ -243,7 +245,7 @@ TEST(Thermostat, heating) { EXPECT_NEAR(thermostat->getTemperature(*container), 10, 1e-6); } -/* +/** * this test the gradual thermostat, so when deltaT is set. In this case its 2.0 * as specified, so we would expect that it converges after at 3k iterations (a * lot earlier actually but this is good enough) if n_thermostat is 100 @@ -251,7 +253,7 @@ TEST(Thermostat, heating) { TEST(Thermostat, gradual) { char arg0[] = "MolSim"; char arg1[] = "-f"; - char arg2[] = "../../../tests/test_cuboid_gradual.xml"; + char arg2[] = "../../tests/test_cuboid_gradual.xml"; char* argv[] = {arg0, arg1, arg2}; auto [name, step, checkpoint] = CLArgumentParser::parse(3, argv); @@ -290,35 +292,36 @@ TEST(Thermostat, gradual) { std::vector> singular_forces; for (auto config : arguments.singular_force_types) { if (std::holds_alternative(config)) { - const auto& [g] = std::get(config); - singular_forces.push_back( - std::move(std::make_unique(g))); + const auto& [g, a] = std::get(config); + singular_forces.push_back(std::make_unique(g, a)); } else { SpdWrapper::get()->error("Unrecognized singular force"); } } - VerletIntegrator verlet_integrator(interactive_forces, singular_forces, - arguments.delta_t); + std::vector> index_forces; + VerletIntegrator verlet_integrator(interactive_forces, singular_forces, + index_forces, arguments.delta_t); std::unique_ptr thermostat; if (arguments.use_thermostat) { thermostat = std::make_unique(arguments.thermostat_config); } - double cur_temp = Thermostat::getTemperature(*container); - EXPECT_NEAR(cur_temp, 1.0, 1); + double cur_temp = thermostat->getTemperature(*container); int diffs_added = 0; - for (std::size_t i = 0; i <= 3000; i++) { + for (std::size_t i = 0; i <= 600; i++) { + // one more application of the thermostat because only with the first + // application the temperature starts to increase effectively without ,v verlet_integrator.step(*container); - if (i > 0 && i % thermostat->n_thermostat == 0) { - std::cout << i << "Iterations " << std::endl; + if (i > 0 && i % thermostat->getNThermostat() == 0) { + std::cout << i << " Iterations " << std::endl; diffs_added++; - double temp = Thermostat::getTemperature(*container); + double temp = thermostat->getTemperature(*container); + std::cout << "Temperature " << temp << std::endl; thermostat->setTemperature(*container); - EXPECT_NEAR(thermostat->getTemperature(*container), - temp + thermostat->d_temp, thermostat->d_temp); } } - EXPECT_NEAR(thermostat->getTemperature(*container), thermostat->T_target, 1); + EXPECT_NEAR(thermostat->getTemperature(*container), thermostat->getTTarget(), + 1e-6); } diff --git a/tests/TruncatedLennardJones.cpp b/tests/TruncatedLennardJones.cpp new file mode 100644 index 00000000..5acd454f --- /dev/null +++ b/tests/TruncatedLennardJones.cpp @@ -0,0 +1,46 @@ +#include "../src/forces/TruncatedLennardJones.h" + +#include + +#include + +#include "../src/defs/containers/ParticleContainer.h" +#include "../src/utils/ArrayUtils.h" +#include "debug/debug_print.h" +#include "testUtil.h" + +/** + * TruncatedLennardJones with distance >= sigma * c + */ +TEST(TruncatedLennardJones, attractive_part) { + Particle p({1, 0, 0}, {0, 0, 0}, 1, 5, 1); + Particle q({0, 1, 0}, {0, 0, 0}, 1, 5, 1); + const TruncatedLennardJones lj; + // cutoff here is 1.1225 * sigma = 1.1225 and dist is sqrt(2) so should be 0 + const dvec3 f = lj.directionalForce(p, q); + DVEC3_NEAR(f, {0, 0, 0}, "Directional force wrong", 1e-5); +} + +/** + * TruncatedLennardJones with a distance < sigma * c + */ +TEST(TruncatedLennardJones, repulsive_part) { + Particle p({1, 0, 0}, {0, 0, 0}, 1, 5, 1); + Particle q({2, 0, 0}, {0, 0, 0}, 1, 5, 1); + const TruncatedLennardJones lj; + // cutoff here is 1.1225 * sigma = 1.1225 and dist is 1 so should be > 0 + const dvec3 f = lj.directionalForce(p, q); + DVEC3_NEAR(f, {-120, 0, 0}, "Directional force wrong", 1e-5); +} + +/** + * TruncatedLennardJones at almost the cutoff + */ +TEST(TruncatedLennardJones, cutoff_part) { + Particle p({0, 0, 0}, {0, 0, 0}, 1, 5, 1); + Particle q({1.1224, 0, 0}, {0, 0, 0}, 1, 5, 1); + const TruncatedLennardJones lj; + // cutoff here is 1.1225 * sigma = 1.1225 and dist is 1.1224 so should be > 0 + const dvec3 f = lj.directionalForce(p, q); + DVEC3_NEAR(f, {-0.01774,0,0}, "Directional force wrong", 1e-5); +} \ No newline at end of file diff --git a/tests/VerletIntegratorTest.cpp b/tests/VerletIntegratorTest.cpp index 1e273745..9db59c0e 100644 --- a/tests/VerletIntegratorTest.cpp +++ b/tests/VerletIntegratorTest.cpp @@ -8,7 +8,7 @@ #include "forces/SingularGravity.h" #include "testUtil.h" -/* +/** * Positions correct after one step, arbitrary example 1 */ TEST(VerletIntegrator, step1) { @@ -18,7 +18,10 @@ TEST(VerletIntegrator, step1) { std::vector> interactive_forces; interactive_forces.push_back(std::make_unique()); std::vector> singular_forces; - VerletIntegrator integrator(interactive_forces, singular_forces, 0.01f); + std::vector> index_forces; + + VerletIntegrator integrator(interactive_forces, singular_forces, index_forces, + 0.01f); p.setF({0, 1, 0}); container.addParticle(p); @@ -33,22 +36,24 @@ TEST(VerletIntegrator, step1) { DVEC3_NEAR(p.getV(), {1, 0.005, 0}, "Velocity wrong.", 1e-5f); } -/* +/** * Positions correct after one step, arbitrary example 2 */ TEST(VerletIntegrator, step2) { DirectSumContainer container; - Particle p({1, 0, 0}, {0, 0, 0}, 1, 5, 1); - Particle q({0, 1, 0}, {0, 0, 0}, 1, 5, 1); + Particle p({1, 0, 0}, {0, 0, 0}, 1, 5, 1, 0); + Particle q({0, 1, 0}, {0, 0, 0}, 1, 5, 1, 0); LennardJones lj; std::vector> interactive_forces; interactive_forces.push_back(std::make_unique()); std::vector> singular_forces; - VerletIntegrator integrator(interactive_forces, singular_forces, 0.01f); + std::vector> index_forces; + + VerletIntegrator integrator(interactive_forces, singular_forces, index_forces, + 0.01f); p.setF({0, 1, 0}); - container.addParticle(p); - container.addParticle(q); + container.addParticles({p, q}); ASSERT_EQ(container.size(), 2); integrator.step(container); diff --git a/tests/WallTest.cpp b/tests/WallTest.cpp new file mode 100644 index 00000000..89b12561 --- /dev/null +++ b/tests/WallTest.cpp @@ -0,0 +1,74 @@ +// +// Created by maximilian on 22.01.25. +// +#include + +#include "calc/VerletIntegrator.h" +#include "defs/Particle.h" +#include "defs/containers/LinkedCellsContainer.h" +#include "forces/LennardJones.h" +#include "forces/SingularGravity.h" +#include "testUtil.h" +#include "utils/ArrayUtils.h" + +/** + * tests that wall particles do not move + */ +TEST(Wall, immovable) { + LinkedCellsContainer container( + {.domain = {3, 3, 3}, // better safe than sorry + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + }}); + + Particle p({1, 1, 1}, {1, 1, 1}, 1, 1, 1, -1); + container.addParticles({p}); + EXPECT_EQ(container.size(), 1) << "Number of Particles is not 0"; + + std::vector> interactive_forces; + interactive_forces.push_back(std::make_unique()); + + std::vector> singular_forces; + singular_forces.push_back(std::make_unique(1, 1)); + + std::vector> index_forces; + + VerletIntegrator v(interactive_forces, singular_forces, index_forces, 0.005); + for (int i = 0; i < 3; i++) { + v.step(container); + } + + container.singleIterator([this](Particle& p) { + DVEC3_NEAR(p.getX(), {1, 1, 1}, "Particle Moved", 1e-8); + }); +} + +/** + * Tests that walls are excluded from Thermostats + */ +TEST(Wall, excludedFromThermostat) { + LinkedCellsContainer container( + {.domain = {3, 3, 3}, // better safe than sorry + .cutoff_radius = 1, + .boundary_config = { + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + LinkedCellsConfig::BoundaryType::Outflow, + }}); + + Particle wall({1, 1, 1}, {1, 1, 1}, 1, 1, 1, -1); + Particle q({2, 2, 2}, {2, 2, 2}, 1, 1, 1, 1); + + container.addParticles({wall}); + container.addParticles({q}); + EXPECT_EQ(container.size(), 2) << "Number of Particles is not 0"; +} \ No newline at end of file diff --git a/tests/XmlReaderTest.cpp b/tests/XmlReaderTest.cpp index 2578d284..5875a512 100644 --- a/tests/XmlReaderTest.cpp +++ b/tests/XmlReaderTest.cpp @@ -3,19 +3,18 @@ #include #include "../src/io/file/in/xml/XmlReader.h" -#if 1 -#include "../src/io/file/in/xml/input.cxx" // It wants this idk why -#include "../src/io/file/in/xml/input.hxx" +// #include "io/file/in/xml/input.cxx" // <- this is necessary at least on +// clang +#include "io/file/in/xml/input.hxx" +#include "io/file/out/checkpoint-schema.cxx" // <- Same for this +#include "io/file/out/checkpoint-schema.hxx" #include "spdlog/fmt/bundled/os.h" #include "testUtil.h" -#include "io/file/out/checkpoint-schema.hxx" -#include "io/file/out/checkpoint-schema.cxx" Arguments arguments = { .t_end = 5, .delta_t = 0.0002, - .force_type = Arguments::LennardJones, .container_data = LinkedCellsConfig{.domain = {100, 100, 100}, .cutoff_radius = 3.0, @@ -45,35 +44,39 @@ TEST(XmlReader, failOnNonXml) { /** * @brief checks if cuboids and DirectSum containers are read correctly - * this test will only pass if it is executed via our build script due to hardcoded file paths + * this test will only pass if it is executed via our build script due to + * hardcoded file paths */ TEST(XmlReader, testCuboid) { - std::vector particles; - XmlReader::read(particles, "../../../tests/test_cuboid.xml", arguments); + XmlReader::read(particles, "../../tests/test_cuboid.xml", arguments); EXPECT_EQ(particles.size(), 20); EXPECT_EQ(arguments.t_end, 15); EXPECT_EQ(arguments.delta_t, 0.015); - EXPECT_EQ(arguments.force_type, Arguments::LennardJones); + EXPECT_TRUE(std::holds_alternative( + arguments.interactive_force_types[0])); EXPECT_TRUE( std::holds_alternative(arguments.container_data)); } /** * @brief checks if both cuboids and spheroids are correct, LinkedCellsConfig - * this test will only pass if it is executed via our build script due to hardcoded file paths + * this test will only pass if it is executed via our build script due to + * hardcoded file paths */ TEST(XmlReader, testCuboidSpheroidLinkedCells) { std::vector particles; - XmlReader::read(particles, "../../../tests/test_cuboid_spheroid.xml", arguments); + + XmlReader::read(particles, "../../tests/test_cuboid_spheroid.xml", arguments); EXPECT_EQ(particles.size(), 1257); EXPECT_EQ(arguments.t_end, 15); EXPECT_EQ(arguments.delta_t, 0.015); - EXPECT_EQ(arguments.force_type, Arguments::LennardJones); + EXPECT_TRUE(std::holds_alternative( + arguments.interactive_force_types[0])); EXPECT_TRUE( std::holds_alternative(arguments.container_data)); - const auto &config = std::get(arguments.container_data); + const auto& config = std::get(arguments.container_data); constexpr auto comp = ivec3{400, 400, 1}; EXPECT_IVEC3_EQ(config.domain, comp); EXPECT_EQ(config.cutoff_radius, 3.0); @@ -84,4 +87,57 @@ TEST(XmlReader, testCuboidSpheroidLinkedCells) { EXPECT_EQ(config.boundary_config.z_high, LinkedCellsConfig::Outflow); EXPECT_EQ(config.boundary_config.z_low, LinkedCellsConfig::Outflow); } -#endif \ No newline at end of file + +namespace fs = std::filesystem; + +/** + * checks if the extension is xml + */ +[[nodiscard]] bool isXMLFile(const fs::path& filePath) noexcept { + return filePath.extension() == ".xml"; +} + +/** + * collects all files in the input directory for further testing + */ +void processXMLFilesInInput(std::vector& paths) { + const std::string inputDir = "../../input"; + + try { + if (!fs::exists(inputDir)) { + std::cerr << "Input directory does not exist: " << inputDir << std::endl; + return; + } + for (const auto& entry : fs::directory_iterator(inputDir)) { + if (entry.is_regular_file() && isXMLFile(entry.path()) && + !fs::is_directory(entry.path())) { + paths.emplace_back(entry.path()); + } + } + } catch (const fs::filesystem_error& e) { + std::cerr << "Filesystem error: " << e.what() << std::endl; + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + } +} + +/** + * This tests that all our current and past input files can still be read + */ +TEST(XmlReader, all_inputs_no_error) { + std::vector paths; + processXMLFilesInInput(paths); + + for (const auto& path : paths) { + // because this requires checkpoints that havent been written yet -> these + // are adressed in different tests + if (path != "../../input/week43.xml" && + path != "../../input/week43periodic.xml" && + path != "../../input/checkpoint_test.xml" && + path != "../../input/checkpoint_membrane_test.xml") { + SpdWrapper::get()->info("Path {}", path.c_str()); + std::vector particles; + EXPECT_NO_FATAL_FAILURE(XmlReader::read(particles, path, arguments)); + } + } +} diff --git a/tests/checkpoint_input_membrane_test.xml b/tests/checkpoint_input_membrane_test.xml new file mode 100644 index 00000000..ef4eb381 --- /dev/null +++ b/tests/checkpoint_input_membrane_test.xml @@ -0,0 +1,103 @@ + + + + + + + 148 + 148 + 148 + + 4.0 + + + + + + + + + + + + + + + + + + + + + + + + + -0.001 + 2 + + + 2.2 + 300 + + + + 17 + 24 + 0 + + + 17 + 25 + 0 + + + 18 + 24 + 0 + + + 18 + 25 + 0 + + + + 0 + 0 + 0.8 + + + + + 0.01 + 500 + false + + + + + 0 + 0 + 0 + + + 15 + 15 + 1.5 + + + 50 + 50 + 1 + + 1 + 2.2 + 1.0 + 1.0 + 1.0 + 0.0 + + + diff --git a/tests/checkpoint_input_test.xml b/tests/checkpoint_input_test.xml new file mode 100644 index 00000000..04d84c67 --- /dev/null +++ b/tests/checkpoint_input_test.xml @@ -0,0 +1,67 @@ + + + + + + + 300 + 300 + 1 + + 3.0 + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0005 + 0 + true + + + + + 0.0 + 0.0 + 0.0 + + + 0 + 0 + 0 + + + 4 + 4 + 1 + + 1 + 1.1225 + 1.0 + 5.0 + 1.0 + 0.0 + + + diff --git a/tests/checkpoint_output_membrane_test.xml b/tests/checkpoint_output_membrane_test.xml new file mode 100644 index 00000000..3708b5d8 --- /dev/null +++ b/tests/checkpoint_output_membrane_test.xml @@ -0,0 +1,51 @@ + + + + + + + 300 + 300 + 1 + + 3.0 + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0005 + 0 + true + + ../../input/checkpoint_membrane_test.xml + true + + 50 + 50 + 1 + + + + diff --git a/tests/checkpoint_output_test.xml b/tests/checkpoint_output_test.xml new file mode 100644 index 00000000..7fe1c2da --- /dev/null +++ b/tests/checkpoint_output_test.xml @@ -0,0 +1,51 @@ + + + + + + + 300 + 300 + 1 + + 3.0 + + + + + + + + + + + + + + + + + + + + + + + + + + 0.0005 + 0 + true + + ../../input/checkpoint_test.xml + false + + 0 + 0 + 0 + + + + diff --git a/tests/checkpoint_test.xml b/tests/checkpoint_test.xml new file mode 100644 index 00000000..beae10b6 --- /dev/null +++ b/tests/checkpoint_test.xml @@ -0,0 +1,421 @@ + + + + + + 0 + 0 + 0 + + + -0.171411273958766 + 0.017805682714513 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 0 + 1.1225 + 0 + + + 0.005717886769296 + -0.14097969926348 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 0 + 2.245 + 0 + + + 0.075628398756773 + -0.058227370160875 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 1.1225 + 0 + 0 + + + 0.101671523409665 + -0.058440003785361 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 1.1225 + 1.1225 + 0 + + + -0.010449378899789 + -0.108907912123567 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 1.1225 + 2.245 + 0 + + + -0.02793671721514 + -0.002863948544169 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 2.245 + 0 + 0 + + + 0.080745488421505 + -0.133418314783138 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 2.245 + 1.1225 + 0 + + + -0.048231620436432 + -0.018917515111695 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 2.245 + 2.245 + 0 + + + 0.019038195229484 + 0.001778663250887 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 0 + 3.3675 + 0 + + + -0.160245069741753 + -0.030433177632549 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 1.1225 + 3.3675 + 0 + + + -0.069003056789251 + 0.119263761036313 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 2.245 + 3.3675 + 0 + + + 0.039878898022719 + 0.183932001456814 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 3.3675 + 0 + 0 + + + -0.040381539473182 + -0.124431204741395 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 3.3675 + 1.1225 + 0 + + + 0.075198966907333 + 0.059770937404545 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 3.3675 + 2.245 + 0 + + + 0.01341068438439 + -0.238647772309973 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + + 3.3675 + 3.3675 + 0 + + + -0.028889381162795 + -0.075648394479965 + 0 + + + 0 + 0 + 0 + + + 0 + 0 + 0 + + 1 + 5 + 1 + 1 + + + diff --git a/input/particles.xml b/tests/particles.xml similarity index 100% rename from input/particles.xml rename to tests/particles.xml diff --git a/tests/testUtil.h b/tests/testUtil.h index fb8bc1c8..1cb649e1 100644 --- a/tests/testUtil.h +++ b/tests/testUtil.h @@ -16,6 +16,18 @@ inline void DVEC3_NEAR(const dvec3& a, const dvec3& b, const std::string& error, for (int i = 0; i < 3; ++i) EXPECT_NEAR(a[i], b[i], eps) << error; } + +/** + * @brief checks if vector a is equal to vector b + * + * @param a vector a + * @param b vector b + * @param error error message on failure + */ +inline void ASSERT_EQ_VEC3(const dvec3& a, const dvec3& b, const std::string& error) { + for (int i = 0; i < 3; ++i) EXPECT_EQ(a[i], b[i]) << error; +} + /** * @brief Check if two vectors are the same * @param list1 first list @@ -43,4 +55,15 @@ inline void EXPECT_IVEC3_EQ(const ivec3& a, const ivec3& b) { for (size_t i = 0; i < a.size(); ++i) { EXPECT_EQ(a[i], b[i]) << "Elements at index " << i << " are not equal"; } +} + +/** + * @brief checks if a list contains an element + * @tparam T template type T + * @param elt query element + * @param list list possibly containing elt + */ +template +void VEC_CONTAINS(const T elt, const std::vector& list) { + EXPECT_TRUE(std::find(list.begin(), list.end(), elt)); } \ No newline at end of file diff --git a/input/test_small.xml b/tests/test_small.xml similarity index 100% rename from input/test_small.xml rename to tests/test_small.xml