diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 516ed44..1179f16 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -14,11 +14,11 @@ on: benchmark_config: description: 'Path to benchmark configuration' required: True - default: src/app-feelpp-discr-1/thermal_bridges_case_3.json + default: src/app-feelpp-io/feelpp-io.json plots_config: description: 'Path to plots related configuration' required: True - default: src/app-feelpp-discr-1/heat_plots.json + default: src/app-feelpp-io/plots.json create_pr: description: 'Create Pull Request' type: boolean @@ -42,7 +42,7 @@ jobs: - name: Get machines configuration id: download_systems_config run: | - machine_config=$(curl https://gist.githubusercontent.com/thomas-saigre/50760ea0999fb22dbf623c962a3c7664/raw/1ebe7c248e6d50466e492480cc24928bbc60be3c/systems.json ) + machine_config=$(curl -H "Authorization: token ${{ secrets.CR_PAT }}" https://raw.githubusercontent.com/feelpp/hpc-systems/refs/heads/main/systems.json) machine_config=$(jq --compact-output '.' <<< $machine_config) echo "machine_config=$machine_config" >> $GITHUB_OUTPUT @@ -54,55 +54,50 @@ jobs: filtered_systems_config=$(echo "$machine_config" | jq --arg valid_systems "$valid_systems" ' ($valid_systems | split(",")) as $systems | - with_entries(select([.key] | inside($systems))) | [.[]] + with_entries(select(.key as $k | $systems | index($k))) | [.[]] ') filtered_systems_config=$(jq --compact-output '.' <<< $filtered_systems_config) echo "filtered_systems_config=$filtered_systems_config" >> $GITHUB_OUTPUT - # - name: Filter Available Runners - # id: filter-available-systems - # run: | - # all_runners=$(curl -H "Accept: application/vnd.github+json" \ - # -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - # -H "X-GitHub-Api-Version: 2022-11-28" \ - # https://api.github.com/orgs/numpex/actions/runners - # ) - # echo $all_runners - - # all_runners=$(echo $all_runners | jq -r '.runners') - - # available_systems="[]" - - # while read -r system; do - # tag=$(echo "$system" | jq -r .runner_tag); - # runner=$(echo "$all_runners" | jq --arg tag "$tag" ' - # map(select(.labels[].name | contains($tag))) - # ') - - # if [ "$(echo "$runner" | jq 'length')" -eq 0 ]; then - # echo "No runner found for $tag ..." - # continue - # fi - - # if echo "$runner" | jq -e 'map(select(.status == "online")) | length > 0' >/dev/null; then - # available_systems=$(echo "$available_systems" | jq --argjson sys "$system" '. + [$sys]') - # else - # echo "No online runners for $tag ..." - # fi - # done < <(echo '${{steps.filter-systems.outputs.filtered_systems_config}}' | jq -c '.[]') - - # available_systems=$(jq --compact-output '.' <<< $available_systems) - # echo "available_systems=$available_systems" >> $GITHUB_OUTPUT - - # - name: Set matrix - # id: hpc-systems - # run: | - # echo "matrix=$(jq -c --null-input --argjson config '${{steps.filter-available-systems.outputs.available_systems}}' '{include: $config}')" >> $GITHUB_OUTPUT + - name: Filter Available Runners + id: filter-available-systems + run: | + all_runners=$(curl -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.CR_PAT }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/numpex/apps-feelpp/actions/runners + ) + echo $all_runners + + all_runners=$(echo $all_runners | jq -r '.runners') + + available_systems="[]" + + while read -r system; do + tag=$(echo "$system" | jq -r .runner_tag); + runner=$(echo "$all_runners" | jq --arg tag "$tag" ' + map(select(.labels[].name | contains($tag))) + ') + + if [ "$(echo "$runner" | jq 'length')" -eq 0 ]; then + echo "No runner found for $tag ..." + continue + fi + + if echo "$runner" | jq -e 'map(select(.status == "online")) | length > 0' >/dev/null; then + available_systems=$(echo "$available_systems" | jq --argjson sys "$system" '. + [$sys]') + else + echo "No online runners for $tag ..." + fi + done < <(echo '${{steps.filter-systems.outputs.filtered_systems_config}}' | jq -c '.[]') + + available_systems=$(jq --compact-output '.' <<< $available_systems) + echo "available_systems=$available_systems" >> $GITHUB_OUTPUT - name: Set matrix id: hpc-systems run: | - echo "matrix=$(jq -c --null-input --argjson config '${{steps.filter-systems.outputs.filtered_systems_config}}' '{include: $config}')" >> $GITHUB_OUTPUT + echo "matrix=$(jq -c --null-input --argjson config '${{steps.filter-available-systems.outputs.available_systems}}' '{include: $config}')" >> $GITHUB_OUTPUT benchmark: needs: [factory] @@ -113,46 +108,56 @@ jobs: timeout-minutes: 7200 name: ${{matrix.machine}} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 + + - uses: astral-sh/setup-uv@v5 - name: Create machine config run: | json_machine_config='${{ toJson(matrix.system_config) }}' echo "$json_machine_config" > machine_config.json + - name: Create venv + run: | + uv venv .venv + source .venv/bin/activate + + - name: Install feelpp-benchmarking + run: uv pip install feelpp-benchmarking + + + - name: Print system Usage + run: bash ./system_entrypoints/${{matrix.machine}}.sh + env: + PROJECT_ID: ${{matrix.project_id || ''}} + - name: Execute benchmarks (CLI) if: ${{ matrix.submit == 'cli' }} run: | - ./tmp_system_entrypoints/${{matrix.machine}}.sh source .venv/bin/activate feelpp-benchmarking-exec \ -mc machine_config.json \ -bc ${{ github.event.inputs.benchmark_config || github.event.client_payload.benchmark_config }} \ -pc ${{ github.event.inputs.plots_config || github.event.client_payload.plots_config}} \ -v \ - -rfm="-v --exec-order=name --max-retries=2" + -rfm="-v --exec-order=name" env: - # GIRDER_API_KEY: ${{ secrets.GIRDER }} - PROJECT_ID: ${{matrix.project_id || ''}} GHCRIO_USER: ${{ github.actor }} - GHCRIO_TOKEN: ${{ secrets.CR_PAT_WORKFLOW }} + GHCRIO_TOKEN: ${{ secrets.CR_PAT }} - #TODO TEMPORARY TMP_SYSTEM_ENTRYPOINTS + #TODO TEMPORARY SYSTEM_ENTRYPOINTS - name: Execute benchmarks (slurm) if: ${{ matrix.submit == 'slurm' }} run: | - ./tmp_system_entrypoints/${{matrix.machine}}.sh srun --partition ${{matrix.submit_partition}} \ --time 48:00:00 \ --account ${{matrix.project_id}} \ --qos ${{matrix.qos}} \ --nodes 1 --ntasks 1 --cpus-per-task 1 --ntasks-per-node 1 \ - ./tmp_system_entrypoints/${{matrix.machine}}_launchBenchmark.sh \ + ./system_entrypoints/${{matrix.machine}}_launchBenchmark.sh \ -m machine_config.json \ -b ${{ github.event.inputs.benchmark_config || github.event.client_payload.benchmark_config }} \ -p ${{ github.event.inputs.plots_config || github.event.client_payload.plots_config}} - env: - GIRDER_API_KEY: ${{ secrets.GIRDER }} - name: Parse reports path id: parse_reports_path @@ -170,7 +175,10 @@ jobs: name: Stage results if: ${{ github.event.inputs.create_pr == 'true' || github.event.client_payload.create_pr == 'true' }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 + + - uses: astral-sh/setup-uv@v5 + - name: Download results uses: actions/download-artifact@v6 @@ -181,9 +189,11 @@ jobs: - name: Create Virtual Environment run: | - python3 -m venv .venv + uv venv .venv source .venv/bin/activate - pip3 install -r requirements.txt + + - name: Install feelpp-benchmarking + run: uv pip install feelpp-benchmarking - name: Upload to staging folder run: | @@ -204,7 +214,7 @@ jobs: echo "use_case=$(jq -r '.use_case_name' ${{ github.event.inputs.benchmark_config || github.event.client_payload.benchmark_config }})" >> $GITHUB_OUTPUT - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 with: title: "Add benchmark on ${{ github.event.inputs.hpc-systems }} for ${{ steps.gather_arguments.outputs.executable_name }} - ${{ steps.gather_arguments.outputs.use_case }}" commit-message: "Add benchmark on ${{ github.event.inputs.hpc-systems }} for ${{ steps.gather_arguments.outputs.executable_name }} - ${{ steps.gather_arguments.outputs.use_case }}" @@ -220,9 +230,6 @@ jobs: see https://github.com/peter-evans/create-pull-request for more details reviewers: | JavierCladellas - prudhomm - vincentchabannes - thomas-saigre labels: new-benchmark branch: new-benchmark-${{ github.event.inputs.hpc-systems || github.event.client_payload.hpc-systems }}-${{ steps.gather_arguments.outputs.use_case }} env: @@ -235,7 +242,9 @@ jobs: if: ${{ github.event.inputs.create_website == 'true' || github.event.client_payload.create_website == 'true' }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 + + - uses: astral-sh/setup-uv@v5 - name: Download results uses: actions/download-artifact@v6 @@ -246,9 +255,12 @@ jobs: - name: Create Virtual Environment run: | - python3 -m venv .venv + uv venv .venv source .venv/bin/activate - pip3 install -r requirements.txt + + - name: Install feelpp-benchmarking + run: uv pip install feelpp-benchmarking + - name: Merge website configs run: | source .venv/bin/activate diff --git a/.github/workflows/benchmark_deploy.yml b/.github/workflows/benchmark_deploy.yml new file mode 100644 index 0000000..f01dc5f --- /dev/null +++ b/.github/workflows/benchmark_deploy.yml @@ -0,0 +1,91 @@ +name: Benchmark Deploy + +on: + pull_request: + types: [closed] + +jobs: + accepted: + if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'new-benchmark') + runs-on: self-feelpp + env: + GIRDER_API_KEY: ${{ secrets.GIRDER }} + name: Move to production + steps: + - uses: astral-sh/setup-uv@v5 + + - uses: actions/checkout@v4 + + - name: Create Virtual Environment + run: | + uv venv .venv + source .venv/bin/activate + + - name: Install feelpp-benchmarking + run: uv pip install feelpp-benchmarking + + - name: Download stage reports + run: | + source .venv/bin/activate + source ./girder_deploy_config.sh + feelpp-girder download -gid $staging_folder_id -o ./tmp/ -d + + - name: Download Production config + run: | + source .venv/bin/activate + source ./girder_deploy_config.sh + feelpp-girder download -gid $production_website_config_id -o ./configs_tmp/ -fn production_config.json + + - name: Merge stage configs + run: | + source .venv/bin/activate + merge-json-configs -fp "./tmp/**/website_config.json" -o ./configs_tmp/stage_config.json -u + + - name: Update Stage Config + run: | + source .venv/bin/activate + source ./girder_deploy_config.sh + update-stage-config -stag ./configs_tmp/stage_config.json -prod ./configs_tmp/production_config.json -prodid $production_folder_id + + - name: Merge stage and prod configs + run: | + source .venv/bin/activate + merge-json-configs -fp "./configs_tmp/**/*.json" -o ./configs_tmp/website_config.json + + - name: Upload New Website config + run: | + source .venv/bin/activate + source ./girder_deploy_config.sh + feelpp-girder upload --item ./configs_tmp/website_config.json --girder_id $production_folder_id + + - name: Cleanup stage folder + run: | + source .venv/bin/activate + source ./girder_deploy_config.sh + feelpp-girder move -oid $staging_folder_id -nid $accepted_folder_id + + denied: + if: github.event.pull_request.merged == false && contains(github.event.pull_request.labels.*.name, 'new-benchmark') + runs-on: self-feelpp + env: + GIRDER_API_KEY: ${{ secrets.GIRDER }} + + name: Move to denied + steps: + - uses: actions/checkout@v4 + + - uses: astral-sh/setup-uv@v5 + + - name: Create Virtual Environment + run: | + uv venv .venv + source .venv/bin/activate + + - name: Install feelpp-benchmarking + run: uv pip install feelpp-benchmarking + + - name: Cleanup stage folder + run: | + source .venv/bin/activate + source ./girder_deploy_config.sh + feelpp-girder move -oid $staging_folder_id -nid $denied_folder_id \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ff47d4..fcaf535 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,183 +3,119 @@ name: CI on: push: branches: - - '**' # Push events on all branches - paths-ignore: - - '.github/workflows/init.yml' + - "**" tags: - - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 including semver + - "v*" jobs: + build: + runs-on: self-feelpp + + name: Build deb packages + if: ${{ !contains(github.event.head_commit.message, 'build skip') }} + steps: + - uses: actions/checkout@v4 + + - name: Configure + run: cmake --preset default + + - name: Build + run: cmake --build --preset default - tests: + - name: Test + if: "!contains(github.event.head_commit.message, 'skip tests')" + run: ctest --preset default + + - name: Package + run: cmake --build --preset default -t package + + - name: Upload + uses: actions/upload-artifact@v5 + with: + name: deb-artifacts + path: build/default/assets/feelpp-app-*.deb + + docker: runs-on: self-feelpp - name: integration-tests - if: ${{ !contains(github.event.head_commit.message, 'tests skip') }} + needs: build + name: Build and push docker image + if: ${{ github.ref_name == 'master' || startsWith(github.ref ,'refs/tags/v') || contains(github.event.head_commit.message, '[build container]') }} + outputs: + tag: ${{ steps.tag_extractor.outputs.tag }} steps: - - uses: actions/checkout@v6 - - - name: Install Python dependencies - run: | - python -m venv .venv - source .venv/bin/activate - python3 -m pip install --upgrade pip - python3 -m pip install pytest - python3 -m pip install -r requirements.txt - - - name: Unit tests - run: | - source .venv/bin/activate - # python3 -m pytest - # env: - # GIRDER_API_KEY: ${{secrets.GIRDER}} - - # - name: Compile applications - # run: | - # cmake --preset default - # cmake --build --preset default - - - name: Test benchmarks - run: | - source .venv/bin/activate - feelpp-benchmarking-exec -mc src/config/gaya/gaya.json -bc src/app-feelpp-discr-1/thermal_bridges_case_3.json -pc src/app-feelpp-discr-1/heat_plots.json - echo $? - - # - name: Dry-run reports - # run: | - # source .venv/bin/activate - # feelpp-benchmarking-render --config-file reports/website_config.json - - # - name: Check files - # run: | #TODO: check if not empty (maybe) - # nb_rfm_report_files=$(ls -1q reports/parallelSum/parallel_sum/default|wc -l) - # nb_doc_reports=$(ls -1q docs/modules/ROOT/pages/reports/|wc -l) - # if [ "$nb_rfm_report_files" -ne 1 ] || [ "$nb_doc_reports" -ne 1 ]; then - # echo "Reports where not rendered." - # exit 1 - # fi - - docs: - runs-on: self-antora - name: Build Antora Site - needs: tests - if: ${{ !contains(github.event.head_commit.message, 'docs skip') }} + - uses: actions/checkout@v4 + + - name: Download + uses: actions/download-artifact@v6 + with: + name: deb-artifacts + path: artifacts/ + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + env: + DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index + with: + images: | + ghcr.io/numpex/apps-feelpp + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + labels: | + org.opencontainers.image.url="https://github.com/numpex/apps-feelpp" + org.opencontainers.image.source="https://github.com/numpex/apps-feelpp" + org.opencontainers.image.authors="Cemosis " + + - name: Extract tag + id: tag_extractor + run: | + FIRST_TAG=$(echo "${{ steps.meta.outputs.tags }}" | tr ' ' '\n' | head -n 1) + echo "tag=$FIRST_TAG" >> "$GITHUB_OUTPUT" + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.CR_PAT }} + + - name: Build Image + uses: docker/build-push-action@v6 + with: + platforms: linux/amd64 + context: artifacts + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + file: ./Dockerfile + + apptainer : + runs-on: self-feelpp + name: Build and Push apptainer image + needs: docker + env: + apptainer: /opt/apptainer/latest/bin/apptainer steps: - - - uses: actions/checkout@v6 - - - name: Install credentials - run: echo https://$GITHUB_OAUTH:@github.com > $HOME/.git-credentials - env: - GITHUB_OAUTH: ${{ secrets.CR_PAT_WORKFLOW }} - - - name: Install NPM dependencies - run: npm ci --verbose - - # - name: Download - # uses: actions/download-artifact@v4 - # with: - # name: wheel-artifacts - # path: dist/ - - - name: Install Python dependencies - run: | - python -m venv .venv - source .venv/bin/activate - - # - name: Download website config - # run: | - # source .venv/bin/activate - # # source ./girder_deploy_config.sh - # # feelpp-girder download --girder_id=$production_website_config_id --output_dir=./tmp/ --filename=website_config.json - # env: - # GIRDER_API_KEY: ${{secrets.GIRDER}} - - # - name: Render reports - # run: | - # source .venv/bin/activate - # feelpp-benchmarking-render --config-file=./tmp/website_config.json - # env: - # GIRDER_API_KEY: ${{ secrets.GIRDER }} - - - name: Build Antora Site - run: | - source .venv/bin/activate - npm run antora || true - npm run antora || true - - - name: Deploy - if: ${{ github.ref_name == 'main' }} - uses: JamesIves/github-pages-deploy-action@v4 - with: - branch: gh-pages # The branch the action should deploy to. - folder: public # The folder the action should deploy. - - # deliver: - # runs-on: self-feelpp - # name: Build docker, tag and push - # steps: - # - uses: actions/checkout@v4 - - # - name: Docker meta - # id: meta - # uses: docker/metadata-action@v5 - # with: - # images: | - # ghcr.io/feelpp/benchmarking - # tags: | - # type=ref,event=branch - # type=ref,event=pr - # type=semver,pattern={{version}} - # type=semver,pattern={{major}}.{{minor}} - - # - name: Set up QEMU - # uses: docker/setup-qemu-action@v3 - - # - name: Set up Docker Buildx - # uses: docker/setup-buildx-action@v3 - - # - name: Login to GitHub Container Registry - # uses: docker/login-action@v3 - # with: - # registry: ghcr.io - # username: ${{ github.repository_owner }} - # password: ${{ secrets.CR_PAT }} - - # - name: Build container image - # uses: docker/build-push-action@v6 - # with: - # push: ${{ github.event_name != 'pull_request' }} - # tags: ${{ steps.meta.outputs.tags }} - # labels: ${{ steps.meta.outputs.labels }} - # file: ./Dockerfile - - # release: - # runs-on: self-feelpp - # name: Release - # needs: [tests] - # if: startsWith(github.ref ,'refs/tags/v') - # permissions: - # contents: write - # id-token: write - # steps: - # - uses: actions/checkout@v4 - - # - name: Download Wheel Packages - # uses: actions/download-artifact@v4 - # with: - # name: wheel-artifacts - # path: dist/ - - # - name: Create Release - # uses: softprops/action-gh-release@v2 - # with: - # files: dist/*.whl - # draft: false - # prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') || contains(github.ref, 'preview')}} - # name: Release ${{ github.ref_name }} - # generate_release_notes: true - # tag_name: ${{ github.ref }} - # token: ${{ secrets.GITHUB_TOKEN }} - - # - name: Publish package distributions to PyPI - # uses: pypa/gh-action-pypi-publish@release/v1 + - name: Login to GHCR + run: | + ${{ env.apptainer }} remote login -u ${{ github.repository_owner }} -p ${{ secrets.CR_PAT }} oras://ghcr.io + + - name: Get docker image ${{ needs.docker.outputs.tag }} + run: | + echo "Using tag: ${{ needs.docker.outputs.tag }}" + ${{ env.apptainer }} pull -F apps-feelpp.sif docker://${{ needs.docker.outputs.tag }} + ls -lrt + + - name: Push apptainer image ${{ needs.docker.outputs.tag }} to ghcr.io + run: | + echo "Using tag: ${{ needs.docker.outputs.tag }}" + ${{ env.apptainer }} push apps-feelpp.sif oras://${{ needs.docker.outputs.tag }}-sif diff --git a/.github/workflows/website_deploy.yml b/.github/workflows/website_deploy.yml new file mode 100644 index 0000000..a48692c --- /dev/null +++ b/.github/workflows/website_deploy.yml @@ -0,0 +1,62 @@ +name: Website Deploy + +on: + push: + branches: + - 'main' + pull_request: + branches: ['main'] + workflow_dispatch: + +jobs: + docs: + runs-on: self-antora + name: Build Antora Site + if: "!contains(github.event.head_commit.message, 'docs skip')" + steps: + - uses: actions/checkout@v4 + + - name: Install credentials + run: echo https://$GITHUB_OAUTH:@github.com > $HOME/.git-credentials + env: + GITHUB_OAUTH: ${{ secrets.CR_PAT }} + + - uses: astral-sh/setup-uv@v5 + + - name: Install NPM dependencies + run: npm install + + - name: Create Virtual Environment + run: | + uv venv .venv + source .venv/bin/activate + + - name: Install feelpp-benchmarking + run: uv pip install feelpp-benchmarking + + - name: Download website config + run: | + source .venv/bin/activate + source ./girder_deploy_config.sh + feelpp-girder download --girder_id=$production_website_config_id --output_dir=./tmp/ --filename=website_config.json + env: + GIRDER_API_KEY: ${{secrets.GIRDER}} + + - name: Render reports + run: | + source .venv/bin/activate + feelpp-benchmarking-render --config-file=./tmp/website_config.json + env: + GIRDER_API_KEY: ${{ secrets.GIRDER }} + + - name: Build Antora Site + run: | + source .venv/bin/activate + npm run antora + + - name: Deploy + uses: JamesIves/github-pages-deploy-action@v4 + # if: ${{ github.ref_name == 'main' }} + with: + branch: gh-pages + folder: public diff --git a/.gitignore b/.gitignore index 852aaa3..013934b 100644 --- a/.gitignore +++ b/.gitignore @@ -94,5 +94,10 @@ outputs/ **/.spack-env/* spack.lock +benchmark/* -benchmark/* \ No newline at end of file +#Docs +docs/modules/ROOT/pages/ +!docs/modules/ROOT/pages/index.adoc + +tmp/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index de690f1..4aa859d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,14 @@ # -*- mode: Dockerfile -*- -FROM ghcr.io/feelpp/feelpp:jammy +FROM ghcr.io/feelpp/feelpp:noble USER root COPY . /home/feelpp/ RUN ls -lrtR /home/feelpp -RUN dpkg -i /home/feelpp/*.deb +RUN apt-get update && apt-get install -y /home/feelpp/*.deb USER feelpp +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD feelpp_app_io --help > /dev/null 2>&1 || exit 1 diff --git a/benchmark b/benchmark deleted file mode 120000 index 69d7b69..0000000 --- a/benchmark +++ /dev/null @@ -1 +0,0 @@ -../benchmark/ \ No newline at end of file diff --git a/docs/antora.yml b/docs/antora.yml index a1ae035..66b8390 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -14,4 +14,5 @@ asciidoc: python: Python cpp: C++ nav: - - modules/ROOT/nav.adoc \ No newline at end of file + - modules/ROOT/nav.adoc + - modules/documentation/nav.adoc \ No newline at end of file diff --git a/docs/antora/supplemental-ui/css/figures.css b/docs/antora/supplemental-ui/css/figures.css new file mode 100644 index 0000000..e0aed62 --- /dev/null +++ b/docs/antora/supplemental-ui/css/figures.css @@ -0,0 +1,78 @@ +.figure-container { + position: relative; + margin: 1.5rem auto; + padding: 1rem; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08); +} + +.subfigure-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #f9f9f9; + padding: 1rem; + border: 1px solid #ddd; + border-top: none; + border-radius: 0 0 8px 8px; +} + +.subfigure-container.active { + position: relative; + opacity: 1; + pointer-events: auto; + z-index: 1; +} + +.subfigure-container.inactive { + opacity: 0; + pointer-events: none; + z-index: 0; +} + +/* Tabs container: display buttons inline with a bottom border to indicate grouping */ +.tabs-container { + display: flex; + border-bottom: 2px solid #007acc; + margin-bottom: 0.5rem; +} + +/* Figure tab button styling */ +.figure-tab { + background: transparent; + border: none; + outline: none; + padding: 0.5rem 1rem; + margin-right: 0.3rem; + font-size: 1rem; + color: #007acc; + cursor: pointer; + border-radius: 4px 4px 0 0; + transition: background-color 0.2s ease, color 0.2s ease; +} + +.figure-tab:hover, +.figure-tab.active { + background-color: #007acc; + color: #fff; +} + +.export-container button { + background-color: #007acc; + color: #fff; + border: none; + padding: 0.4rem 0.8rem; + margin-right: 0.4rem; + border-radius: 4px; + cursor: pointer; + font-size: 0.9rem; + transition: background-color 0.2s ease; +} + +.export-container button:hover { + background-color: #005fa3; +} \ No newline at end of file diff --git a/docs/antora/supplemental-ui/css/table.css b/docs/antora/supplemental-ui/css/table.css new file mode 100644 index 0000000..ffbdf67 --- /dev/null +++ b/docs/antora/supplemental-ui/css/table.css @@ -0,0 +1,3 @@ +.scrollable { + overflow-x: auto; +} \ No newline at end of file diff --git a/docs/antora/supplemental-ui/js/figure-helper.js b/docs/antora/supplemental-ui/js/figure-helper.js new file mode 100644 index 0000000..4d31618 --- /dev/null +++ b/docs/antora/supplemental-ui/js/figure-helper.js @@ -0,0 +1,55 @@ +function downloadData(data, filename) { + if (data.length == 1) { + if (data[0].content.length == 1) { + d = data[0].content[0]; + const blob = new Blob([d.data], { type: "plain/text;charset=utf-8" }); + downloadBlob(blob, `${d.title}.${data[0].format}`); + } + else { + downloadFilesAsZip( + [{ format: data[0].format, content: data[0].content }], + filename + ); + } + } + else if (data.length > 1) { + downloadFilesAsZip(data, filename); + } + else { + alert("No data to download"); + } +} + + +function downloadFilesAsZip(data, zipFilename) { + const zip = new JSZip(); + data.forEach((d) => { + d.content.forEach((c) => { + const filename = `${c.title}.${d.format}`; + zip.file(filename, c.data); + }); + }); + zip.generateAsync({ type: "blob" }).then((blob) => downloadBlob(blob, zipFilename)); +} + +function downloadBlob(blob, filename) { + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); +} + +function switchTab(button, tabIndex) { + const figureContainer = button.closest('.figure-container'); + figureContainer.querySelectorAll('.subfigure-container').forEach(subfig => { + subfig.classList.remove('active'); + subfig.classList.add('inactive'); + }); + const activeSubfig = figureContainer.querySelector(`.subfigure-container[data-subfigure='${tabIndex}']`); + activeSubfig.classList.add('active'); + activeSubfig.classList.remove('inactive'); +} diff --git a/docs/antora/supplemental-ui/js/table-filter-sort.js b/docs/antora/supplemental-ui/js/table-filter-sort.js new file mode 100644 index 0000000..bc98a9c --- /dev/null +++ b/docs/antora/supplemental-ui/js/table-filter-sort.js @@ -0,0 +1,71 @@ +// table-filter-sort.js +(function () { + function filterTable(table, input) { + if (!table || !input) return; + + input.addEventListener("input", () => { + const term = input.value.toLowerCase(); + const rows = table.querySelectorAll("tbody tr"); + rows.forEach(row => { + row.style.display = row.textContent.toLowerCase().includes(term) ? "" : "none"; + }); + }); + } + + function sortTable(table) { + if (!table) return; + + const headers = table.querySelectorAll("thead th"); + headers.forEach((header, index) => { + header.style.cursor = "pointer"; + + header.addEventListener("click", () => { + const tbody = table.querySelector("tbody"); + const rows = Array.from(tbody.querySelectorAll("tr")); + const asc = header.dataset.sortOrder !== "asc"; + + // reset arrows + headers.forEach(h => { + h.textContent = h.textContent.replace(/[\u25B2\u25BC]/g, "").trim(); + delete h.dataset.sortOrder; + }); + + // set arrow + header.dataset.sortOrder = asc ? "asc" : "desc"; + header.textContent += asc ? " ▲" : " ▼"; + + // sort rows + rows.sort((a, b) => { + const ta = a.children[index].textContent.trim(); + const tb = b.children[index].textContent.trim(); + const na = parseFloat(ta); + const nb = parseFloat(tb); + + if (!isNaN(na) && !isNaN(nb)) return asc ? na - nb : nb - na; + return asc ? ta.localeCompare(tb) : tb.localeCompare(ta); + }); + + rows.forEach(r => tbody.appendChild(r)); + }); + }); + } + + /** + * Initialize a table with sorting & filtering + * @param {HTMLElement} table - the table element + * @param {string} inputId - ID of the input element + */ + function initTable(table, inputId) { + const input = document.getElementById(inputId); + if (!table || !input) { + console.warn("Table or input not found", table, inputId); + return; + } + + filterTable(table, input); + sortTable(table); + } + + // expose globally + window.initTable = initTable; +})(); diff --git a/docs/antora/supplemental-ui/partials/footer-content.hbs b/docs/antora/supplemental-ui/partials/footer-content.hbs index f20f705..37ab140 100644 --- a/docs/antora/supplemental-ui/partials/footer-content.hbs +++ b/docs/antora/supplemental-ui/partials/footer-content.hbs @@ -19,3 +19,8 @@ + + + + + diff --git a/docs/antora/supplemental-ui/partials/header-content.hbs b/docs/antora/supplemental-ui/partials/header-content.hbs index c4b7c6c..f7c1a97 100644 --- a/docs/antora/supplemental-ui/partials/header-content.hbs +++ b/docs/antora/supplemental-ui/partials/header-content.hbs @@ -1,3 +1,6 @@ + + +