diff --git a/.github/workflows/_reusable-tests.yml b/.github/workflows/_reusable-tests.yml index 47678d7303c..059c223f76d 100644 --- a/.github/workflows/_reusable-tests.yml +++ b/.github/workflows/_reusable-tests.yml @@ -95,13 +95,13 @@ jobs: COV_FLAG="-DWITH_CODE_COV=ON" fi cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON ${COV_FLAG} - cmake --build build + cmake --build build -j $(nproc) - name: Configure and compile MNE-CPP (MacOS) if: matrix.os == 'macos-26' run: | cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON - cmake --build build + cmake --build build -j $(sysctl -n hw.logicalcpu) - name: Configure and compile MNE-CPP (Windows) if: matrix.os == 'windows-2025' @@ -109,13 +109,13 @@ jobs: cmd.exe /c "call `"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat`" && set > %temp%\vcvars.txt" Get-Content "$env:temp\vcvars.txt" | Foreach-Object { if ($_ -match "^(.*?)=(.*)$") { Set-Content "env:\$($matches[1])" $matches[2] } } cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON - cmake --build build --config Release + cmake --build build --config Release --parallel # ── Run tests ────────────────────────────────────────────────────────── - name: Run tests (Linux) if: matrix.os == 'ubuntu-24.04' env: - QTEST_FUNCTION_TIMEOUT: 900000 + QTEST_FUNCTION_TIMEOUT: ${{ inputs.with_code_coverage && 1800000 || 900000 }} QT_QPA_PLATFORM: offscreen MNE_DATA: ~/mne_data run: | @@ -150,26 +150,33 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} # ── Coverage: threshold & regression check (staging branch) ──────────── - - name: Install lcov + - name: Install fastcov if: matrix.os == 'ubuntu-24.04' && inputs.with_code_coverage && !inputs.upload_codecov - run: sudo apt-get install -y -qq lcov + run: pip install fastcov - - name: Generate lcov report + - name: Generate coverage report (fastcov) id: lcov if: matrix.os == 'ubuntu-24.04' && inputs.with_code_coverage && !inputs.upload_codecov run: | - lcov --capture --directory build \ - --output-file coverage.info \ - --ignore-errors mismatch 2>/dev/null || true - # Remove external/test code from the report - lcov --remove coverage.info \ - '*/src/external/*' '*/src/examples/*' '*/src/testframes/*' \ - '/usr/*' '*/Qt/*' \ - --output-file coverage_filtered.info \ - --ignore-errors unused 2>/dev/null || true - # Extract total line coverage percentage - COVERAGE=$(lcov --summary coverage_filtered.info 2>&1 \ - | grep -oP 'lines\.*:\s*\K[0-9]+(\.[0-9]+)?' || echo "0") + echo "=== Coverage diagnostics ===" + echo "gcno files: $(find build -name '*.gcno' 2>/dev/null | wc -l)" + echo "gcda files: $(find build -name '*.gcda' 2>/dev/null | wc -l)" + # Collect coverage data with fastcov (10-100x faster than lcov) + fastcov -d build -o coverage.json + # Full report in lcov format + fastcov -C coverage.json --lcov -o coverage.info + # Filtered report: only MNE-CPP libraries (no apps, tests, examples, external, Qt, Eigen) + fastcov -C coverage.json --lcov -o coverage_filtered.info \ + --include src/libraries/ \ + --exclude /usr Qt eigen + # Extract total line coverage percentage from lcov format + TOTAL_LINES=$(grep -c "^DA:" coverage_filtered.info 2>/dev/null || echo "0") + HIT_LINES=$(grep "^DA:" coverage_filtered.info | awk -F, '$2 > 0' | wc -l 2>/dev/null || echo "0") + if [ "$TOTAL_LINES" -gt 0 ]; then + COVERAGE=$(awk "BEGIN {printf \"%.2f\", $HIT_LINES * 100.0 / $TOTAL_LINES}") + else + COVERAGE="0" + fi echo "Total line coverage: ${COVERAGE}%" echo "coverage=${COVERAGE}" >> "$GITHUB_OUTPUT" @@ -206,16 +213,23 @@ jobs: fi fi - # --- Regression check --- + # --- Regression check (allow up to 2% drop) --- if [ "$CHECK_REGRESSION" = "true" ] && [ -f .coverage-baseline ]; then PREVIOUS=$(cat .coverage-baseline) echo " Previous coverage: ${PREVIOUS}%" - if (( $(echo "${CURRENT} < ${PREVIOUS}" | bc -l) )); then + ALLOWED_DROP="2.0" + LIMIT=$(echo "${PREVIOUS} - ${ALLOWED_DROP}" | bc -l) + if (( $(echo "${CURRENT} < ${LIMIT}" | bc -l) )); then DELTA=$(echo "${PREVIOUS} - ${CURRENT}" | bc -l) - echo " ❌ FAIL: Coverage dropped by ${DELTA}% (${PREVIOUS}% → ${CURRENT}%)" + echo " ❌ FAIL: Coverage dropped by ${DELTA}% (${PREVIOUS}% → ${CURRENT}%), exceeds ${ALLOWED_DROP}% tolerance" FAILED=true else - echo " ✅ PASS: Coverage did not regress" + if (( $(echo "${CURRENT} < ${PREVIOUS}" | bc -l) )); then + DELTA=$(echo "${PREVIOUS} - ${CURRENT}" | bc -l) + echo " ⚠️ WARN: Coverage dropped by ${DELTA}% (within ${ALLOWED_DROP}% tolerance)" + else + echo " ✅ PASS: Coverage did not regress" + fi fi fi diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1171f1b81fa..4c59f7f1fb3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,7 +22,7 @@ # # Branch model: Feature → Staging → Main # ═══════════════════════════════════════════════════════════════════════════ -name: Main +name: Release on: push: @@ -84,13 +84,11 @@ jobs: - name: Deploy qch to GitHub Release if: startsWith(github.ref, 'refs/tags/') - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: doc/doxygen/qt-creator_doc/mne-cpp.qch - asset_name: mne-cpp-${{ github.ref_name }}-doc-qtcreator.qch - tag: ${{ github.ref }} - overwrite: true + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp doc/doxygen/qt-creator_doc/mne-cpp.qch "mne-cpp-${{ github.ref_name }}-doc-qtcreator.qch" + gh release upload "${{ github.ref_name }}" "mne-cpp-${{ github.ref_name }}-doc-qtcreator.qch" --clobber - name: Deploy Doxygen to GitHub Pages (production) env: @@ -294,13 +292,11 @@ jobs: tar cfz mne-cpp-wasm.tar.gz -C out/wasm . - name: Upload WASM to GitHub Release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: mne-cpp-wasm.tar.gz - asset_name: mne-cpp-${{ github.ref_name }}-wasm.tar.gz - tag: ${{ github.ref }} - overwrite: true + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp mne-cpp-wasm.tar.gz "mne-cpp-${{ github.ref_name }}-wasm.tar.gz" + gh release upload "${{ github.ref_name }}" "mne-cpp-${{ github.ref_name }}-wasm.tar.gz" --clobber - name: Deploy WASM to mne-cpp/wasm repository env: diff --git a/.github/workflows/release-installer.yml b/.github/workflows/release-installer.yml index 822d471811b..fafa295eee7 100644 --- a/.github/workflows/release-installer.yml +++ b/.github/workflows/release-installer.yml @@ -205,14 +205,11 @@ jobs: ls -lh MNE-CPP-*.run - name: Upload installer - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: MNE-CPP-*-Linux.run - file_glob: true - asset_name: ${{ steps.tag.outputs.asset_prefix }}-linux-installer-x86_64.run - tag: ${{ steps.tag.outputs.tag }} - overwrite: true + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp MNE-CPP-*-Linux.run "${{ steps.tag.outputs.asset_prefix }}-linux-installer-x86_64.run" + gh release upload "${{ steps.tag.outputs.tag }}" "${{ steps.tag.outputs.asset_prefix }}-linux-installer-x86_64.run" --clobber # ========================================================================= # macOS Installer (.dmg) @@ -540,14 +537,11 @@ jobs: security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true - name: Upload installer - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: MNE-CPP-*-Darwin.dmg - file_glob: true - asset_name: ${{ steps.tag.outputs.asset_prefix }}-macos-installer-arm64.dmg - tag: ${{ steps.tag.outputs.tag }} - overwrite: true + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp MNE-CPP-*-Darwin.dmg "${{ steps.tag.outputs.asset_prefix }}-macos-installer-arm64.dmg" + gh release upload "${{ steps.tag.outputs.tag }}" "${{ steps.tag.outputs.asset_prefix }}-macos-installer-arm64.dmg" --clobber # ========================================================================= # Windows Installer (.exe) @@ -734,11 +728,9 @@ jobs: Get-ChildItem MNE-CPP-*-win64.exe - name: Upload installer - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: MNE-CPP-*-win64.exe - file_glob: true - asset_name: ${{ steps.tag.outputs.asset_prefix }}-windows-installer-x86_64.exe - tag: ${{ steps.tag.outputs.tag }} - overwrite: true + shell: bash + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp MNE-CPP-*-win64.exe "${{ steps.tag.outputs.asset_prefix }}-windows-installer-x86_64.exe" + gh release upload "${{ steps.tag.outputs.tag }}" "${{ steps.tag.outputs.asset_prefix }}-windows-installer-x86_64.exe" --clobber diff --git a/.github/workflows/release-linux.yml b/.github/workflows/release-linux.yml index ef06b0385a0..e30d7f4417b 100644 --- a/.github/workflows/release-linux.yml +++ b/.github/workflows/release-linux.yml @@ -58,13 +58,11 @@ jobs: ./tools/deploy.bat dynamic pack - name: Upload release binaries if: steps.release-info.outputs.should_upload == 'true' - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: mne-cpp-linux-dynamic-x86_64.tar.gz - asset_name: ${{ steps.release-info.outputs.prefix }}-linux-dynamic-x86_64.tar.gz - tag: ${{ steps.release-info.outputs.tag }} - overwrite: true + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp mne-cpp-linux-dynamic-x86_64.tar.gz "${{ steps.release-info.outputs.prefix }}-linux-dynamic-x86_64.tar.gz" + gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-linux-dynamic-x86_64.tar.gz" --clobber LinuxStatic: @@ -141,10 +139,8 @@ jobs: ./tools/deploy.bat static pack - name: Upload release binaries if: steps.release-info.outputs.should_upload == 'true' - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: mne-cpp-linux-static-x86_64.tar.gz - asset_name: ${{ steps.release-info.outputs.prefix }}-linux-static-x86_64.tar.gz - tag: ${{ steps.release-info.outputs.tag }} - overwrite: true + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp mne-cpp-linux-static-x86_64.tar.gz "${{ steps.release-info.outputs.prefix }}-linux-static-x86_64.tar.gz" + gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-linux-static-x86_64.tar.gz" --clobber diff --git a/.github/workflows/release-macos.yml b/.github/workflows/release-macos.yml index 41c1312b6a3..fe2672b8cee 100644 --- a/.github/workflows/release-macos.yml +++ b/.github/workflows/release-macos.yml @@ -77,13 +77,11 @@ jobs: fi - name: Upload release binaries if: steps.release-info.outputs.should_upload == 'true' - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: mne-cpp-macos-dynamic-arm64.tar.gz - asset_name: ${{ steps.release-info.outputs.prefix }}-macos-dynamic-arm64.tar.gz - tag: ${{ steps.release-info.outputs.tag }} - overwrite: true + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp mne-cpp-macos-dynamic-arm64.tar.gz "${{ steps.release-info.outputs.prefix }}-macos-dynamic-arm64.tar.gz" + gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-macos-dynamic-arm64.tar.gz" --clobber MacOSStatic: @@ -159,10 +157,8 @@ jobs: ./tools/deploy.bat static pack - name: Upload release binaries if: steps.release-info.outputs.should_upload == 'true' - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: mne-cpp-macos-static-arm64.tar.gz - asset_name: ${{ steps.release-info.outputs.prefix }}-macos-static-arm64.tar.gz - tag: ${{ steps.release-info.outputs.tag }} - overwrite: true + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp mne-cpp-macos-static-arm64.tar.gz "${{ steps.release-info.outputs.prefix }}-macos-static-arm64.tar.gz" + gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-macos-static-arm64.tar.gz" --clobber diff --git a/.github/workflows/release-windows.yml b/.github/workflows/release-windows.yml index 0460a82659c..2ba71d96b01 100644 --- a/.github/workflows/release-windows.yml +++ b/.github/workflows/release-windows.yml @@ -72,13 +72,12 @@ jobs: } - name: Upload release binaries if: steps.release-info.outputs.should_upload == 'true' - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: mne-cpp-windows-dynamic-x86_64.zip - asset_name: ${{ steps.release-info.outputs.prefix }}-windows-dynamic-x86_64.zip - tag: ${{ steps.release-info.outputs.tag }} - overwrite: true + shell: bash + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp mne-cpp-windows-dynamic-x86_64.zip "${{ steps.release-info.outputs.prefix }}-windows-dynamic-x86_64.zip" + gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-windows-dynamic-x86_64.zip" --clobber WinStatic: @@ -156,10 +155,9 @@ jobs: ./tools/deploy.bat static pack - name: Upload release binaries if: steps.release-info.outputs.should_upload == 'true' - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: mne-cpp-windows-static-x86_64.zip - asset_name: ${{ steps.release-info.outputs.prefix }}-windows-static-x86_64.zip - tag: ${{ steps.release-info.outputs.tag }} - overwrite: true + shell: bash + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp mne-cpp-windows-static-x86_64.zip "${{ steps.release-info.outputs.prefix }}-windows-static-x86_64.zip" + gh release upload "${{ steps.release-info.outputs.tag }}" "${{ steps.release-info.outputs.prefix }}-windows-static-x86_64.zip" --clobber diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 5a4b6e51f5e..c38f17c840e 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -33,9 +33,9 @@ concurrency: cancel-in-progress: true env: - # Minimum acceptable line coverage percentage. + # Minimum acceptable line coverage percentage (libraries only). # The pipeline will FAIL if coverage drops below this. - COVERAGE_THRESHOLD: 50 + COVERAGE_THRESHOLD: 35 jobs: @@ -100,13 +100,15 @@ jobs: qt_version: '6.10.2' # ── Tests with coverage threshold & regression checks ──────────────────── + # Add [skip-coverage] to your commit message to skip coverage analysis. + # Coverage is only collected on Ubuntu; macOS/Windows always run without it. Tests: uses: ./.github/workflows/_reusable-tests.yml with: - with_code_coverage: true + with_code_coverage: ${{ !contains(github.event.head_commit.message, '[skip-coverage]') }} upload_codecov: false - coverage_threshold: 50 - fail_on_coverage_regression: true + coverage_threshold: ${{ contains(github.event.head_commit.message, '[skip-coverage]') && 0 || 35 }} + fail_on_coverage_regression: ${{ !contains(github.event.head_commit.message, '[skip-coverage]') }} # ── Doxygen API documentation (dev) ────────────────────────────────────── Doxygen: @@ -131,13 +133,11 @@ jobs: doxygen mne-cpp_doxyfile - name: Deploy qch to dev_build release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: doc/doxygen/qt-creator_doc/mne-cpp.qch - asset_name: mne-cpp-dev-doc-qtcreator.qch - tag: dev_build - overwrite: true + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp doc/doxygen/qt-creator_doc/mne-cpp.qch mne-cpp-dev-doc-qtcreator.qch + gh release upload dev_build mne-cpp-dev-doc-qtcreator.qch --clobber - name: Deploy Doxygen to GitHub Pages (dev subdirectory) env: @@ -262,10 +262,8 @@ jobs: tar cfz mne-cpp-wasm.tar.gz -C out/wasm . - name: Upload WASM to dev_build release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} - file: mne-cpp-wasm.tar.gz - asset_name: mne-cpp-dev-wasm.tar.gz - tag: dev_build - overwrite: true + env: + GH_TOKEN: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }} + run: | + cp mne-cpp-wasm.tar.gz mne-cpp-dev-wasm.tar.gz + gh release upload dev_build mne-cpp-dev-wasm.tar.gz --clobber diff --git a/README.md b/README.md index 6b673113234..bb3e0ba4bc6 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

- Main + Release Staging diff --git a/codecov.yml b/codecov.yml index f288100145a..b76c64f3f62 100644 --- a/codecov.yml +++ b/codecov.yml @@ -34,3 +34,4 @@ ignore: - "src/external/**" # third-party libraries (e.g. Eigen) - "src/examples/**" # example code is not part of coverage - "src/testframes/**" # test harnesses themselves + - "src/applications/**" # applications are not part of library coverage diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cf376bcbbd7..24142e2637e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,7 +63,7 @@ if(USE_FFTW) endif() if(WITH_CODE_COV) - add_compile_options("--coverage") + add_compile_options("--coverage" "-O1" "-g") add_link_options("--coverage") endif() diff --git a/src/applications/mne_analyze/libs/anShared/Model/dipolefitmodel.h b/src/applications/mne_analyze/libs/anShared/Model/dipolefitmodel.h index 9ef0121cb64..362ddf8562a 100644 --- a/src/applications/mne_analyze/libs/anShared/Model/dipolefitmodel.h +++ b/src/applications/mne_analyze/libs/anShared/Model/dipolefitmodel.h @@ -43,7 +43,7 @@ #include "../Utils/types.h" #include "abstractmodel.h" -#include +#include //============================================================================================================= // QT INCLUDES diff --git a/src/applications/mne_analyze/libs/anShared/Utils/metatypes.h b/src/applications/mne_analyze/libs/anShared/Utils/metatypes.h index be8c84470ab..78aa60f0e1e 100644 --- a/src/applications/mne_analyze/libs/anShared/Utils/metatypes.h +++ b/src/applications/mne_analyze/libs/anShared/Utils/metatypes.h @@ -37,7 +37,7 @@ #ifndef METATYPES_H #define METATYPES_H -#include +#include #include #include "../Management/event.h" #include "../Model/fiffrawviewmodel.h" diff --git a/src/applications/mne_analyze/libs/anShared/Utils/types.h b/src/applications/mne_analyze/libs/anShared/Utils/types.h index ea9124889f6..a938e2320c9 100644 --- a/src/applications/mne_analyze/libs/anShared/Utils/types.h +++ b/src/applications/mne_analyze/libs/anShared/Utils/types.h @@ -40,7 +40,7 @@ // INCLUDES //============================================================================================================= -#include +#include #include //============================================================================================================= diff --git a/src/applications/mne_analyze/plugins/dipolefit/dipolefit.cpp b/src/applications/mne_analyze/plugins/dipolefit/dipolefit.cpp index 2f7db8827ea..e50f2bcc668 100644 --- a/src/applications/mne_analyze/plugins/dipolefit/dipolefit.cpp +++ b/src/applications/mne_analyze/plugins/dipolefit/dipolefit.cpp @@ -53,9 +53,9 @@ #include -#include -#include -#include +#include +#include +#include #include diff --git a/src/applications/mne_analyze/plugins/dipolefit/dipolefit.h b/src/applications/mne_analyze/plugins/dipolefit/dipolefit.h index 163d28d37dc..19719a3eaa7 100644 --- a/src/applications/mne_analyze/plugins/dipolefit/dipolefit.h +++ b/src/applications/mne_analyze/plugins/dipolefit/dipolefit.h @@ -42,7 +42,7 @@ #include "dipolefit_global.h" #include -#include +#include //============================================================================================================= // QT INCLUDES diff --git a/src/applications/mne_browse/Models/averagemodel.h b/src/applications/mne_browse/Models/averagemodel.h index 77dc469f501..b15d7c4ebdf 100644 --- a/src/applications/mne_browse/Models/averagemodel.h +++ b/src/applications/mne_browse/Models/averagemodel.h @@ -51,6 +51,7 @@ //============================================================================================================= #include +#include //************************************************************************************************************* diff --git a/src/applications/mne_browse/Models/eventmodel.h b/src/applications/mne_browse/Models/eventmodel.h index 9b24fbec614..6a8989a3272 100644 --- a/src/applications/mne_browse/Models/eventmodel.h +++ b/src/applications/mne_browse/Models/eventmodel.h @@ -51,6 +51,7 @@ #include #include #include +#include #include #include diff --git a/src/applications/mne_compute_mne/main.cpp b/src/applications/mne_compute_mne/main.cpp index 3ba9ae7c9f4..27b8094e42b 100644 --- a/src/applications/mne_compute_mne/main.cpp +++ b/src/applications/mne_compute_mne/main.cpp @@ -52,14 +52,14 @@ #include #include -#include -#include -#include +#include +#include +#include #include -#include +#include -#include +#include #include @@ -90,6 +90,7 @@ using namespace Eigen; using namespace FIFFLIB; using namespace MNELIB; +using namespace FWDLIB; using namespace INVERSELIB; using namespace FSLIB; using namespace UTILSLIB; @@ -431,7 +432,7 @@ static bool writeDipFile(const QString &fileName, * @return true on success. */ static bool writePredictedData(const QString &fileName, - const MNEForwardSolution &forward, + const FwdForwardSolution &forward, const MatrixXd &stcData, const VectorXd ×Vec) { @@ -857,7 +858,7 @@ int main(int argc, char *argv[]) printf(" Source amplitude: %.1f nAm\n", fwdAmp * 1e9); QFile fwdFile(fwdName); - MNEForwardSolution fwd(fwdFile); + FwdForwardSolution fwd(fwdFile); if (!fwd.sol || fwd.sol->data.rows() == 0) { qCritical() << "Error: Could not read forward solution from" << fwdName; @@ -1264,13 +1265,13 @@ int main(int argc, char *argv[]) printf("\nComputing predicted sensor data...\n"); // Read forward solution for prediction - MNEForwardSolution predFwd; + FwdForwardSolution predFwd; if (parser.isSet(predfwdOpt)) { QFile fwdFile(parser.value(predfwdOpt)); - predFwd = MNEForwardSolution(fwdFile, false, true); + predFwd = FwdForwardSolution(fwdFile, false, true); } else if (useFwdAsData) { QFile fwdFile(parser.value(fwdOpt)); - predFwd = MNEForwardSolution(fwdFile, false, true); + predFwd = FwdForwardSolution(fwdFile, false, true); } else { printf(" NOTE: --pred requires a forward solution.\n"); printf(" Use --predfwd to specify one, or use --fwd mode.\n"); diff --git a/src/applications/mne_compute_raw_inverse/main.cpp b/src/applications/mne_compute_raw_inverse/main.cpp index 026657bd00c..0df82a3cb62 100644 --- a/src/applications/mne_compute_raw_inverse/main.cpp +++ b/src/applications/mne_compute_raw_inverse/main.cpp @@ -50,13 +50,13 @@ #include #include -#include -#include +#include +#include #include -#include +#include -#include +#include #include diff --git a/src/applications/mne_dipole_fit/main.cpp b/src/applications/mne_dipole_fit/main.cpp index efa64e9c75a..fa95bf773ab 100644 --- a/src/applications/mne_dipole_fit/main.cpp +++ b/src/applications/mne_dipole_fit/main.cpp @@ -37,8 +37,8 @@ // INCLUDES //============================================================================================================= -#include -#include +#include +#include #include @@ -50,9 +50,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include diff --git a/src/applications/mne_flash_bem/flashbem.cpp b/src/applications/mne_flash_bem/flashbem.cpp index 1263849fad0..ec627b52e52 100644 --- a/src/applications/mne_flash_bem/flashbem.cpp +++ b/src/applications/mne_flash_bem/flashbem.cpp @@ -44,7 +44,7 @@ #include "flashbem.h" #include "mne_flash_bem_settings.h" -#include +#include #include #include diff --git a/src/applications/mne_forward_solution/main.cpp b/src/applications/mne_forward_solution/main.cpp index 7022158355e..77baa303cc7 100644 --- a/src/applications/mne_forward_solution/main.cpp +++ b/src/applications/mne_forward_solution/main.cpp @@ -28,7 +28,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * - * @brief Implements the mne_dipole_fit application. + * @brief Implements the mne_forward_solution application. * */ @@ -36,10 +36,9 @@ // INCLUDES //============================================================================================================= -#include -#include - -#include +#include +#include +#include #include @@ -48,6 +47,8 @@ //============================================================================================================= #include +#include +#include //============================================================================================================= // USED NAMESPACES @@ -72,11 +73,177 @@ int main(int argc, char *argv[]) { qInstallMessageHandler(UTILSLIB::ApplicationLogger::customLogWriter); QApplication app(argc, argv); + app.setApplicationVersion("2.10"); + + //========================================================================================================= + // Command line parser + //========================================================================================================= + + QCommandLineParser parser; + parser.setApplicationDescription( + "Compute the MEG/EEG forward solution.\n" + "Port of the original MNE-C mne_forward_solution by Matti Hamalainen." + ); + parser.addHelpOption(); + parser.addVersionOption(); + + // Source & sensor options + QCommandLineOption srcOpt("src", "Source space file.", "file"); + QCommandLineOption measOpt("meas", "MEG/EEG measurement file (sensor & electrode locations).", "file"); + QCommandLineOption fwdOpt("fwd", "Output forward solution file.", "file"); + + // Transform options + QCommandLineOption mriOpt("mri", "MRI description file for head/MRI transform.", "file"); + QCommandLineOption transOpt("trans", "Text file for head/MRI transform.", "file"); + QCommandLineOption notransOpt("notrans", "Head and MRI coordinate systems are identical."); + + // BEM & sphere model options + QCommandLineOption bemOpt("bem", "BEM model file.", "file"); + QCommandLineOption originOpt("origin", "Sphere model origin in head coordinates (x:y:z in mm).", "x:y:z"); + QCommandLineOption eegscalpOpt("eegscalp", "Scale electrode locations to the scalp surface (sphere model)."); + QCommandLineOption eegmodelsOpt("eegmodels", "File of EEG sphere model specifications.", "file"); + QCommandLineOption eegmodelOpt("eegmodel", "Name of the EEG sphere model to use (default: Default).", "name"); + QCommandLineOption eegradOpt("eegrad", "Scalp radius for EEG sphere model in mm (default: 90.0).", "radius"); + + // Modality flags + QCommandLineOption megOpt("meg", "Compute the MEG forward solution."); + QCommandLineOption eegOpt("eeg", "Compute the EEG forward solution."); + QCommandLineOption gradOpt("grad", "Compute the gradient of the field with respect to dipole coordinates."); + QCommandLineOption fixedOpt("fixed", "Calculate only for the source orientation given by surface normals."); + QCommandLineOption accurateOpt("accurate", "Use accurate coil definitions in MEG forward computation."); + QCommandLineOption mricoordOpt("mricoord", "Do calculations in MRI coordinates instead of head coordinates."); + QCommandLineOption allOpt("all", "Calculate forward solution in all nodes instead of selected ones only."); + + // Source space filtering + QCommandLineOption labelOpt("label", "Label file to select sources (can be specified multiple times).", "file"); + QCommandLineOption mindistOpt("mindist", "Minimum distance of sources from the inner skull surface (mm).", "dist"); + QCommandLineOption mindistoutOpt("mindistout", "Output file for omitted source space points.", "file"); + QCommandLineOption includeallOpt("includeall", "Omit all source space checks."); + + parser.addOptions({srcOpt, measOpt, fwdOpt, + mriOpt, transOpt, notransOpt, + bemOpt, originOpt, eegscalpOpt, eegmodelsOpt, eegmodelOpt, eegradOpt, + megOpt, eegOpt, gradOpt, fixedOpt, accurateOpt, mricoordOpt, allOpt, + labelOpt, mindistOpt, mindistoutOpt, includeallOpt}); + + parser.process(app); + + //========================================================================================================= + // Populate settings from parsed arguments + //========================================================================================================= + + ComputeFwdSettings::SPtr settings = ComputeFwdSettings::SPtr(new ComputeFwdSettings()); + + // Build the command string for FIFF stamping + settings->command = QCoreApplication::arguments().join(" "); + + // Source & sensor files + if (parser.isSet(srcOpt)) + settings->srcname = parser.value(srcOpt); + if (parser.isSet(measOpt)) + settings->measname = parser.value(measOpt); + if (parser.isSet(fwdOpt)) + settings->solname = parser.value(fwdOpt); + + // Transform options (--mri and --trans are mutually exclusive with --notrans) + if (parser.isSet(notransOpt)) { + settings->mri_head_ident = true; + settings->mriname.clear(); + settings->transname.clear(); + } else if (parser.isSet(mriOpt)) { + settings->mri_head_ident = false; + settings->mriname = parser.value(mriOpt); + settings->transname.clear(); + } else if (parser.isSet(transOpt)) { + settings->mri_head_ident = false; + settings->transname = parser.value(transOpt); + settings->mriname.clear(); + } + + // BEM model + if (parser.isSet(bemOpt)) + settings->bemname = parser.value(bemOpt); + + // Sphere model origin (x:y:z in mm, converted to meters) + if (parser.isSet(originOpt)) { + QStringList parts = parser.value(originOpt).split(':'); + if (parts.size() != 3) { + qCritical("Could not interpret the origin."); + return 1; + } + bool ok1, ok2, ok3; + settings->r0[0] = parts[0].toFloat(&ok1) / 1000.0f; + settings->r0[1] = parts[1].toFloat(&ok2) / 1000.0f; + settings->r0[2] = parts[2].toFloat(&ok3) / 1000.0f; + if (!ok1 || !ok2 || !ok3) { + qCritical("Could not interpret the origin."); + return 1; + } + } + + // EEG sphere model options + if (parser.isSet(eegscalpOpt)) + settings->scale_eeg_pos = true; + if (parser.isSet(eegmodelsOpt)) + settings->eeg_model_file = parser.value(eegmodelsOpt); + if (parser.isSet(eegmodelOpt)) + settings->eeg_model_name = parser.value(eegmodelOpt); + if (parser.isSet(eegradOpt)) { + bool ok; + float rad = parser.value(eegradOpt).toFloat(&ok); + if (!ok || rad <= 0) { + qCritical("Radius must be a positive number."); + return 1; + } + settings->eeg_sphere_rad = rad / 1000.0f; + } + + // Modality flags + if (parser.isSet(megOpt)) + settings->include_meg = true; + if (parser.isSet(eegOpt)) + settings->include_eeg = true; + if (parser.isSet(gradOpt)) + settings->compute_grad = true; + if (parser.isSet(fixedOpt)) + settings->fixed_ori = true; + if (parser.isSet(accurateOpt)) + settings->accurate = true; + if (parser.isSet(mricoordOpt)) + settings->coord_frame = FIFFV_COORD_MRI; + if (parser.isSet(allOpt)) + settings->do_all = true; + + // Source space filtering + if (parser.isSet(labelOpt)) { + settings->labels = parser.values(labelOpt); + settings->nlabel = settings->labels.size(); + } + if (parser.isSet(mindistOpt)) { + bool ok; + float dist = parser.value(mindistOpt).toFloat(&ok); + if (!ok) { + qCritical("Could not interpret the distance."); + return 1; + } + settings->mindist = (dist <= 0.0f) ? 0.0f : dist / 1000.0f; + } + if (parser.isSet(mindistoutOpt)) + settings->mindistoutname = parser.value(mindistoutOpt); + if (parser.isSet(includeallOpt)) + settings->filter_spaces = false; + + //========================================================================================================= + // Run forward computation + //========================================================================================================= + + settings->checkIntegrity(); + + ComputeFwd computer(settings); + auto fwdSolution = computer.calculateFwd(); - ComputeFwdSettings::SPtr settings = ComputeFwdSettings::SPtr(new ComputeFwdSettings(&argc,argv)); - ComputeFwd cmpFwd(settings); - cmpFwd.calculateFwd(); - cmpFwd.storeFwd(); + QFile fwdFile(settings->solname); + fwdSolution->write(fwdFile); return app.exec(); } diff --git a/src/applications/mne_inspect/app/mainwindow.cpp b/src/applications/mne_inspect/app/mainwindow.cpp index 7d2ec4f1f79..bf0b56ca8fd 100644 --- a/src/applications/mne_inspect/app/mainwindow.cpp +++ b/src/applications/mne_inspect/app/mainwindow.cpp @@ -41,9 +41,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include diff --git a/src/applications/mne_inverse_operator/main.cpp b/src/applications/mne_inverse_operator/main.cpp index 2760132446d..750a876065d 100644 --- a/src/applications/mne_inverse_operator/main.cpp +++ b/src/applications/mne_inverse_operator/main.cpp @@ -57,8 +57,8 @@ #include #include -#include -#include +#include +#include #include @@ -87,6 +87,8 @@ using namespace Eigen; using namespace FIFFLIB; using namespace MNELIB; +using namespace FWDLIB; +using namespace INVERSELIB; using namespace UTILSLIB; //============================================================================================================= @@ -105,7 +107,7 @@ using namespace UTILSLIB; * and populates SSP projectors from the noise covariance and/or --proj files. * This mirrors the original MNE-C approach where no measurement file is needed. */ -static FiffInfo buildInfoFromForward(const MNEForwardSolution &forward, +static FiffInfo buildInfoFromForward(const FwdForwardSolution &forward, const FiffCov &noiseCov, const QList &extraProjs) { @@ -427,7 +429,7 @@ int main(int argc, char *argv[]) printf("Reading forward solution from %s...\n", fwdName.toUtf8().constData()); QFile fwdFile(fwdName); - MNEForwardSolution forward(fwdFile, false, true); + FwdForwardSolution forward(fwdFile, false, true); if (forward.isEmpty()) { qCritical() << "Error: Could not read forward solution from" << fwdName; diff --git a/src/applications/mne_make_source_space/main.cpp b/src/applications/mne_make_source_space/main.cpp index 696d10d7a94..a92bdc82784 100644 --- a/src/applications/mne_make_source_space/main.cpp +++ b/src/applications/mne_make_source_space/main.cpp @@ -49,8 +49,8 @@ #include #include -#include -#include +#include +#include #include diff --git a/src/applications/mne_scan/libs/scDisp/realtime3dwidget.cpp b/src/applications/mne_scan/libs/scDisp/realtime3dwidget.cpp index 0d704b11c77..86cfb6aa570 100644 --- a/src/applications/mne_scan/libs/scDisp/realtime3dwidget.cpp +++ b/src/applications/mne_scan/libs/scDisp/realtime3dwidget.cpp @@ -65,6 +65,8 @@ #include +#include + //============================================================================================================= // QT INCLUDES //============================================================================================================= diff --git a/src/applications/mne_scan/libs/scDisp/realtime3dwidget.h b/src/applications/mne_scan/libs/scDisp/realtime3dwidget.h index 2b621036a52..09e5b915757 100644 --- a/src/applications/mne_scan/libs/scDisp/realtime3dwidget.h +++ b/src/applications/mne_scan/libs/scDisp/realtime3dwidget.h @@ -46,8 +46,8 @@ #include -#include -#include +#include +#include //============================================================================================================= // QT INCLUDES diff --git a/src/applications/mne_scan/libs/scMeas/CMakeLists.txt b/src/applications/mne_scan/libs/scMeas/CMakeLists.txt index a6b0ef85fc9..5b379aba93c 100644 --- a/src/applications/mne_scan/libs/scMeas/CMakeLists.txt +++ b/src/applications/mne_scan/libs/scMeas/CMakeLists.txt @@ -74,7 +74,9 @@ set(MNE_LIBS_REQUIRED mne_utils mne_fiff mne_fs + mne_fwd mne_mne + mne_inverse mne_connectivity ) diff --git a/src/applications/mne_scan/libs/scMeas/realtimeconnectivityestimate.cpp b/src/applications/mne_scan/libs/scMeas/realtimeconnectivityestimate.cpp index a7fdfb5344c..bc2a0208624 100644 --- a/src/applications/mne_scan/libs/scMeas/realtimeconnectivityestimate.cpp +++ b/src/applications/mne_scan/libs/scMeas/realtimeconnectivityestimate.cpp @@ -40,10 +40,10 @@ #include -#include +#include -#include -#include +#include +#include #include @@ -58,6 +58,7 @@ using namespace SCMEASLIB; using namespace CONNECTIVITYLIB; using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; //============================================================================================================= @@ -68,7 +69,7 @@ RealTimeConnectivityEstimate::RealTimeConnectivityEstimate(QObject *parent) : Measurement(QMetaType::fromName("RealTimeConnectivityEstimate::SPtr").id(), parent) , m_pAnnotSet(AnnotationSet::SPtr(new AnnotationSet)) , m_pSurfSet(SurfaceSet::SPtr(new SurfaceSet)) -, m_pFwdSolution(MNEForwardSolution::SPtr(new MNEForwardSolution)) +, m_pFwdSolution(FwdForwardSolution::SPtr(new FwdForwardSolution)) , m_pNetwork(Network::SPtr(new Network)) , m_bInitialized(false) { diff --git a/src/applications/mne_scan/libs/scMeas/realtimeconnectivityestimate.h b/src/applications/mne_scan/libs/scMeas/realtimeconnectivityestimate.h index f3fc1f3841b..60cf6cb7234 100644 --- a/src/applications/mne_scan/libs/scMeas/realtimeconnectivityestimate.h +++ b/src/applications/mne_scan/libs/scMeas/realtimeconnectivityestimate.h @@ -62,8 +62,11 @@ namespace FIFFLIB { class FiffInfo; } +namespace FWDLIB { + class FwdForwardSolution; +} + namespace MNELIB { - class MNEForwardSolution; class MNEBem; } @@ -159,7 +162,7 @@ class SCMEASSHARED_EXPORT RealTimeConnectivityEstimate : public Measurement * * @param[in] fwdSolution the forward solution to set. */ - inline void setFwdSolution(const QSharedPointer& fwdSolution); + inline void setFwdSolution(const QSharedPointer& fwdSolution); //========================================================================================================= /** @@ -167,7 +170,7 @@ class SCMEASSHARED_EXPORT RealTimeConnectivityEstimate : public Measurement * * @return the forward solution. */ - inline QSharedPointer& getFwdSolution(); + inline QSharedPointer& getFwdSolution(); //========================================================================================================= /** @@ -218,7 +221,7 @@ class SCMEASSHARED_EXPORT RealTimeConnectivityEstimate : public Measurement QSharedPointer m_pAnnotSet; /**< Annotation set. Needed for visualization. */ QSharedPointer m_pSurfSet; /**< Surface set. Needed for visualization. */ - QSharedPointer m_pFwdSolution; /**< Forward solution. Needed for visualization. */ + QSharedPointer m_pFwdSolution; /**< Forward solution. Needed for visualization. */ QSharedPointer m_pSensorSurface; /**< The sensor surface. Needed for visualization. */ QSharedPointer m_pNetwork; /**< The network/connectivity estimate. */ @@ -277,7 +280,7 @@ inline QSharedPointer& RealTimeConnectivityEstimate::getSurfS //============================================================================================================= -inline void RealTimeConnectivityEstimate::setFwdSolution(const QSharedPointer& fwdSolution) +inline void RealTimeConnectivityEstimate::setFwdSolution(const QSharedPointer& fwdSolution) { QMutexLocker locker(&m_qMutex); m_pFwdSolution = fwdSolution; @@ -285,7 +288,7 @@ inline void RealTimeConnectivityEstimate::setFwdSolution(const QSharedPointer& RealTimeConnectivityEstimate::getFwdSolution() +inline QSharedPointer& RealTimeConnectivityEstimate::getFwdSolution() { QMutexLocker locker(&m_qMutex); return m_pFwdSolution; diff --git a/src/applications/mne_scan/libs/scMeas/realtimefwdsolution.cpp b/src/applications/mne_scan/libs/scMeas/realtimefwdsolution.cpp index 4065e786d9f..b59c218338b 100644 --- a/src/applications/mne_scan/libs/scMeas/realtimefwdsolution.cpp +++ b/src/applications/mne_scan/libs/scMeas/realtimefwdsolution.cpp @@ -37,7 +37,7 @@ //============================================================================================================= #include "realtimefwdsolution.h" -#include +#include #include //============================================================================================================= @@ -51,6 +51,7 @@ using namespace SCMEASLIB; using namespace FIFFLIB; using namespace MNELIB; +using namespace FWDLIB; //============================================================================================================= // DEFINE MEMBER METHODS @@ -60,7 +61,7 @@ RealTimeFwdSolution::RealTimeFwdSolution(QObject *parent) : Measurement(QMetaType::fromName("RealTimeFwdSolution::SPtr").id(), parent) , m_bInitialized(false) , m_bClustered(false) -, m_pFwdSolution(QSharedPointer(new MNEForwardSolution)) +, m_pFwdSolution(QSharedPointer(new FwdForwardSolution)) { } @@ -86,7 +87,7 @@ QSharedPointer RealTimeFwdSolution::getFiffInfo() //============================================================================================================= -QSharedPointer RealTimeFwdSolution::getValue() +QSharedPointer RealTimeFwdSolution::getValue() { QMutexLocker locker(&m_qMutex); return m_pFwdSolution; @@ -94,7 +95,7 @@ QSharedPointer RealTimeFwdSolution::getValue() //============================================================================================================= -void RealTimeFwdSolution::setValue(const MNEForwardSolution::SPtr pFwdSolution) +void RealTimeFwdSolution::setValue(const FwdForwardSolution::SPtr pFwdSolution) { m_qMutex.lock(); m_pFwdSolution = pFwdSolution; diff --git a/src/applications/mne_scan/libs/scMeas/realtimefwdsolution.h b/src/applications/mne_scan/libs/scMeas/realtimefwdsolution.h index 3446deb9f40..386235e7fc4 100644 --- a/src/applications/mne_scan/libs/scMeas/realtimefwdsolution.h +++ b/src/applications/mne_scan/libs/scMeas/realtimefwdsolution.h @@ -42,7 +42,7 @@ #include "scmeas_global.h" #include "measurement.h" -#include +#include //============================================================================================================= // QT INCLUDES @@ -63,8 +63,8 @@ namespace FIFFLIB{ class FiffInfo; } -namespace MNELIB{ - class MNEForwardSolution; +namespace FWDLIB{ + class FwdForwardSolution; } //============================================================================================================= @@ -122,7 +122,7 @@ class SCMEASSHARED_EXPORT RealTimeFwdSolution : public Measurement * * @param[in] fwdSolution The forward solution which should be distributed. */ - virtual void setValue(const MNELIB::MNEForwardSolution::SPtr pFwdSolution); + virtual void setValue(const FWDLIB::FwdForwardSolution::SPtr pFwdSolution); //========================================================================================================= /** @@ -131,7 +131,7 @@ class SCMEASSHARED_EXPORT RealTimeFwdSolution : public Measurement * * @return The last attached value. */ - virtual QSharedPointer getValue(); + virtual QSharedPointer getValue(); //========================================================================================================= /** @@ -186,7 +186,7 @@ class SCMEASSHARED_EXPORT RealTimeFwdSolution : public Measurement bool m_bInitialized; /**< If values are stored.*/ bool m_bClustered; /**< If fwd is clustered.*/ - QSharedPointer m_pFwdSolution; /**< The Mne Forward Solution. */ + QSharedPointer m_pFwdSolution; /**< The Mne Forward Solution. */ QSharedPointer m_pFiffInfo; /**< The Fiff Info. */ QSharedDataPointer m_pNamedMatSol; /**< The solution matrix (LF). */ diff --git a/src/applications/mne_scan/libs/scMeas/realtimehpiresult.h b/src/applications/mne_scan/libs/scMeas/realtimehpiresult.h index 50fc5775639..897faa23955 100644 --- a/src/applications/mne_scan/libs/scMeas/realtimehpiresult.h +++ b/src/applications/mne_scan/libs/scMeas/realtimehpiresult.h @@ -42,7 +42,7 @@ #include "scmeas_global.h" #include "measurement.h" -#include +#include //============================================================================================================= // QT INCLUDES diff --git a/src/applications/mne_scan/libs/scMeas/realtimesourceestimate.cpp b/src/applications/mne_scan/libs/scMeas/realtimesourceestimate.cpp index adb8d5d0374..97e4db47414 100644 --- a/src/applications/mne_scan/libs/scMeas/realtimesourceestimate.cpp +++ b/src/applications/mne_scan/libs/scMeas/realtimesourceestimate.cpp @@ -49,6 +49,8 @@ using namespace SCMEASLIB; using namespace MNELIB; +using namespace FWDLIB; +using namespace INVERSELIB; using namespace FSLIB; //============================================================================================================= @@ -59,7 +61,7 @@ RealTimeSourceEstimate::RealTimeSourceEstimate(QObject *parent) : Measurement(QMetaType::fromName("RealTimeSourceEstimate::SPtr").id(), parent) , m_pAnnotSet(AnnotationSet::SPtr(new AnnotationSet)) , m_pSurfSet(SurfaceSet::SPtr(new SurfaceSet)) -, m_pFwdSolution(MNEForwardSolution::SPtr(new MNEForwardSolution)) +, m_pFwdSolution(FwdForwardSolution::SPtr(new FwdForwardSolution)) , m_iSourceEstimateSize(1) , m_bInitialized(false) { diff --git a/src/applications/mne_scan/libs/scMeas/realtimesourceestimate.h b/src/applications/mne_scan/libs/scMeas/realtimesourceestimate.h index a87321176e9..39d2d491c30 100644 --- a/src/applications/mne_scan/libs/scMeas/realtimesourceestimate.h +++ b/src/applications/mne_scan/libs/scMeas/realtimesourceestimate.h @@ -43,16 +43,16 @@ #include "scmeas_global.h" #include "measurement.h" -#include -#include -#include +#include +#include +#include #include #include #include -#include -#include +#include +#include //============================================================================================================= // QT INCLUDES @@ -151,7 +151,7 @@ class SCMEASSHARED_EXPORT RealTimeSourceEstimate : public Measurement * * @param[in] fwdSolution the forward solution to set. */ - inline void setFwdSolution(MNELIB::MNEForwardSolution::SPtr& fwdSolution); + inline void setFwdSolution(FWDLIB::FwdForwardSolution::SPtr& fwdSolution); //========================================================================================================= /** @@ -159,7 +159,7 @@ class SCMEASSHARED_EXPORT RealTimeSourceEstimate : public Measurement * * @return the forward solution. */ - inline MNELIB::MNEForwardSolution::SPtr& getFwdSolution(); + inline FWDLIB::FwdForwardSolution::SPtr& getFwdSolution(); //========================================================================================================= /** @@ -168,7 +168,7 @@ class SCMEASSHARED_EXPORT RealTimeSourceEstimate : public Measurement * * @param[in] v the value which is attached to the sample array vector. */ - virtual void setValue(MNELIB::MNESourceEstimate &v); + virtual void setValue(INVERSELIB::MNESourceEstimate &v); //========================================================================================================= /** @@ -177,7 +177,7 @@ class SCMEASSHARED_EXPORT RealTimeSourceEstimate : public Measurement * * @return the last attached value. */ - virtual QList& getValue(); + virtual QList& getValue(); //========================================================================================================= /** @@ -227,11 +227,11 @@ class SCMEASSHARED_EXPORT RealTimeSourceEstimate : public Measurement FSLIB::AnnotationSet::SPtr m_pAnnotSet; /**< Annotation set. */ FSLIB::SurfaceSet::SPtr m_pSurfSet; /**< Surface set. */ - MNELIB::MNEForwardSolution::SPtr m_pFwdSolution; /**< Forward solution. */ + FWDLIB::FwdForwardSolution::SPtr m_pFwdSolution; /**< Forward solution. */ qint32 m_iSourceEstimateSize; /**< Sample size of the multi sample array.*/ - QList m_pMNEStc; /**< The source estimates. */ + QList m_pMNEStc; /**< The source estimates. */ bool m_bInitialized; /**< Is initialized. */ }; @@ -286,7 +286,7 @@ inline FIFFLIB::FiffCoordTrans& RealTimeSourceEstimate::getMriHeadTrans() } //============================================================================================================= -inline void RealTimeSourceEstimate::setFwdSolution(MNELIB::MNEForwardSolution::SPtr& fwdSolution) +inline void RealTimeSourceEstimate::setFwdSolution(FWDLIB::FwdForwardSolution::SPtr& fwdSolution) { QMutexLocker locker(&m_qMutex); m_pFwdSolution = fwdSolution; @@ -294,7 +294,7 @@ inline void RealTimeSourceEstimate::setFwdSolution(MNELIB::MNEForwardSolution::S //============================================================================================================= -inline MNELIB::MNEForwardSolution::SPtr& RealTimeSourceEstimate::getFwdSolution() +inline FWDLIB::FwdForwardSolution::SPtr& RealTimeSourceEstimate::getFwdSolution() { QMutexLocker locker(&m_qMutex); return m_pFwdSolution; diff --git a/src/applications/mne_scan/plugins/hpi/hpi.cpp b/src/applications/mne_scan/plugins/hpi/hpi.cpp index c281c008d0c..47f6fbfaf1b 100644 --- a/src/applications/mne_scan/plugins/hpi/hpi.cpp +++ b/src/applications/mne_scan/plugins/hpi/hpi.cpp @@ -46,8 +46,8 @@ #include #include #include -#include -#include +#include +#include #include #include diff --git a/src/applications/mne_scan/plugins/rtcmne/FormFiles/rtcmnesetupwidget.cpp b/src/applications/mne_scan/plugins/rtcmne/FormFiles/rtcmnesetupwidget.cpp index fb87df4d708..240ac356be1 100644 --- a/src/applications/mne_scan/plugins/rtcmne/FormFiles/rtcmnesetupwidget.cpp +++ b/src/applications/mne_scan/plugins/rtcmne/FormFiles/rtcmnesetupwidget.cpp @@ -41,11 +41,11 @@ #include "../rtcmne.h" -#include -#include +#include +#include #include -#include +#include #include diff --git a/src/applications/mne_scan/plugins/rtcmne/rtcmne.cpp b/src/applications/mne_scan/plugins/rtcmne/rtcmne.cpp index 3801d465f82..eb413bdc42d 100644 --- a/src/applications/mne_scan/plugins/rtcmne/rtcmne.cpp +++ b/src/applications/mne_scan/plugins/rtcmne/rtcmne.cpp @@ -43,16 +43,16 @@ #include -#include -#include +#include +#include #include -#include -#include +#include +#include #include -#include +#include #include diff --git a/src/applications/mne_scan/plugins/rtcmne/rtcmne.h b/src/applications/mne_scan/plugins/rtcmne/rtcmne.h index 83c36037120..a81a1f8d3a5 100644 --- a/src/applications/mne_scan/plugins/rtcmne/rtcmne.h +++ b/src/applications/mne_scan/plugins/rtcmne/rtcmne.h @@ -48,12 +48,13 @@ #include -#include +#include //============================================================================================================= // QT INCLUDES //============================================================================================================= +#include #include #include #include @@ -66,9 +67,8 @@ namespace DISPLIB { class MinimumNormSettingsView; } -namespace MNELIB { - class MNEForwardSolution; - class MNEInverseOperator; +namespace FWDLIB { + class FwdForwardSolution; } namespace FIFFLIB { @@ -77,6 +77,7 @@ namespace FIFFLIB { } namespace INVERSELIB { + class MNEInverseOperator; class MinimumNorm; } @@ -189,7 +190,7 @@ class RTCMNESHARED_EXPORT RtcMne : public SCSHAREDLIB::AbstractAlgorithm * * @param[in] invOp The inverse operator to update. */ - void updateInvOp(const MNELIB::MNEInverseOperator& invOp); + void updateInvOp(const INVERSELIB::MNEInverseOperator& invOp); //========================================================================================================= /** @@ -232,7 +233,7 @@ class RTCMNESHARED_EXPORT RtcMne : public SCSHAREDLIB::AbstractAlgorithm QSharedPointer m_pCircularMatrixBuffer; /**< Holds incoming RealTimeMultiSampleArray data.*/ QSharedPointer > m_pCircularEvokedBuffer; /**< Holds incoming RealTimeMultiSampleArray data.*/ QSharedPointer m_pRtInvOp; /**< Real-time inverse operator. */ - QSharedPointer m_pFwd; /**< Forward solution. */ + QSharedPointer m_pFwd; /**< Forward solution. */ QSharedPointer m_pNoiseCov; /**< Noise Covariance Matrix. */ QSharedPointer m_pAnnotationSet; /**< Annotation set. */ QSharedPointer m_pSurfaceSet; /**< Surface set. */ @@ -263,7 +264,7 @@ class RTCMNESHARED_EXPORT RtcMne : public SCSHAREDLIB::AbstractAlgorithm QStringList m_qListCovChNames; /**< Covariance channel names. */ QStringList m_qListPickChannels; /**< Channels to pick. */ - MNELIB::MNEInverseOperator m_invOp; /**< The inverse operator. */ + INVERSELIB::MNEInverseOperator m_invOp; /**< The inverse operator. */ signals: void responsibleTriggerTypesChanged(const QStringList& lResponsibleTriggerTypes); diff --git a/src/applications/mne_scan/plugins/rtfwd/FormFiles/rtfwdsetupwidget.cpp b/src/applications/mne_scan/plugins/rtfwd/FormFiles/rtfwdsetupwidget.cpp index 8c884400d98..f307c161f10 100644 --- a/src/applications/mne_scan/plugins/rtfwd/FormFiles/rtfwdsetupwidget.cpp +++ b/src/applications/mne_scan/plugins/rtfwd/FormFiles/rtfwdsetupwidget.cpp @@ -40,7 +40,7 @@ #include -#include +#include //============================================================================================================= // QT INCLUDES diff --git a/src/applications/mne_scan/plugins/rtfwd/rtfwd.cpp b/src/applications/mne_scan/plugins/rtfwd/rtfwd.cpp index 1be8a4ce057..aab02243351 100644 --- a/src/applications/mne_scan/plugins/rtfwd/rtfwd.cpp +++ b/src/applications/mne_scan/plugins/rtfwd/rtfwd.cpp @@ -40,14 +40,14 @@ #include -#include -#include +#include +#include -#include +#include -#include +#include -#include +#include #include #include @@ -59,6 +59,7 @@ #include #include +#include //============================================================================================================= // EIGEN INCLUDES @@ -425,11 +426,10 @@ void RtFwd::run() // initialize fwd solution emit statusInformationChanged(0); // initializing - ComputeFwd::SPtr pComputeFwd = ComputeFwd::SPtr(new ComputeFwd(m_pFwdSettings)); + ComputeFwd::SPtr pFwdComputer = ComputeFwd::SPtr(new ComputeFwd(m_pFwdSettings)); - QFile t_fSolution(m_pFwdSettings->solname); - MNEForwardSolution::SPtr pFwdSolution; - MNEForwardSolution::SPtr pClusteredFwd; + FwdForwardSolution::SPtr pFwdSolution; + FwdForwardSolution::SPtr pClusteredFwd; emit statusInformationChanged(4); // not computed @@ -456,11 +456,10 @@ void RtFwd::run() m_mutex.unlock(); // compute and store - pComputeFwd->calculateFwd(); - pComputeFwd->storeFwd(); + pFwdSolution.reset(pFwdComputer->calculateFwd().release()); - // get Mne Forward Solution (in future this is not necessary, ComputeForward will have this as member) - pFwdSolution = MNEForwardSolution::SPtr(new MNEForwardSolution(t_fSolution, false, true)); + QFile fwdFile(m_pFwdSettings->solname); + pFwdSolution->write(fwdFile); // emit results to control widget emit fwdSolutionAvailable(pFwdSolution->source_ori, @@ -503,9 +502,7 @@ void RtFwd::run() transMegHead = m_pHpiFitResult->devHeadTrans; m_mutex.unlock(); - pComputeFwd->updateHeadPos(transMegHead); - pFwdSolution->sol = pComputeFwd->sol; - pFwdSolution->sol_grad = pComputeFwd->sol_grad; + pFwdComputer->updateHeadPos(transMegHead, *pFwdSolution); m_mutex.lock(); m_bBusy = false; @@ -528,7 +525,7 @@ void RtFwd::run() if(bDoClustering && bFwdReady && bNClusterChanged) { emit statusInformationChanged(3); // clustering - pClusteredFwd = MNEForwardSolution::SPtr(new MNEForwardSolution(pFwdSolution->cluster_forward_solution(*m_pAnnotationSet.data(), m_pFwdSettings->ncluster))); + pClusteredFwd = FwdForwardSolution::SPtr(new FwdForwardSolution(pFwdSolution->cluster_forward_solution(*m_pAnnotationSet.data(), m_pFwdSettings->ncluster))); emit clusteringAvailable(pClusteredFwd->nsource); m_pRTFSOutput->measurementData()->setValue(pClusteredFwd); diff --git a/src/applications/mne_scan/plugins/rtfwd/rtfwd.h b/src/applications/mne_scan/plugins/rtfwd/rtfwd.h index f4872eb4344..676ab8243cf 100644 --- a/src/applications/mne_scan/plugins/rtfwd/rtfwd.h +++ b/src/applications/mne_scan/plugins/rtfwd/rtfwd.h @@ -75,8 +75,8 @@ namespace FWDLIB { class ComputeFwd; } -namespace MNELIB{ - class MNEForwardSolution; +namespace FWDLIB{ + class FwdForwardSolution; } namespace FSLIB{ diff --git a/src/applications/mne_setup_forward_model/setupforwardmodel.cpp b/src/applications/mne_setup_forward_model/setupforwardmodel.cpp index 3e24180e13e..0acf260d0db 100644 --- a/src/applications/mne_setup_forward_model/setupforwardmodel.cpp +++ b/src/applications/mne_setup_forward_model/setupforwardmodel.cpp @@ -54,7 +54,7 @@ #include "setupforwardmodel.h" #include "mne_setup_forward_model_settings.h" -#include +#include #include #include @@ -662,7 +662,7 @@ bool SetupForwardModel::prepareBemSolution(const QString& bemFile, // 2. Compute the linear collocation solution // 3. Save the BEM model with the solution to a new FIFF file // - FwdBemModel* bemModel = nullptr; + std::unique_ptr bemModel; if (m_settings.homogeneous()) { bemModel = FwdBemModel::fwd_bem_load_homog_surface(const_cast(bemFile)); @@ -677,10 +677,9 @@ bool SetupForwardModel::prepareBemSolution(const QString& bemFile, printf("Computing the linear collocation solution...\n"); - int result = FwdBemModel::fwd_bem_compute_solution(bemModel, FWD_BEM_LINEAR_COLL); + int result = bemModel->fwd_bem_compute_solution(FWD_BEM_LINEAR_COLL); if (result != 0) { qCritical() << "BEM solution computation failed."; - delete bemModel; return false; } @@ -711,7 +710,7 @@ bool SetupForwardModel::prepareBemSolution(const QString& bemFile, for (int k = 0; k < bemModel->nsurf; ++k) { stream->start_block(FIFFB_BEM_SURF); - MNESurface* s = bemModel->surfs[k]; + MNESurface* s = bemModel->surfs[k].get(); // Surface ID int surfId = s->id; @@ -762,14 +761,8 @@ bool SetupForwardModel::prepareBemSolution(const QString& bemFile, } // Write the solution matrix - if (bemModel->solution && bemModel->nsol > 0) { - MatrixXf solMat(bemModel->nsol, bemModel->nsol); - for (int i = 0; i < bemModel->nsol; ++i) { - for (int j = 0; j < bemModel->nsol; ++j) { - solMat(i, j) = bemModel->solution[i][j]; - } - } - stream->write_float_matrix(FIFF_BEM_POT_SOLUTION, solMat); + if (bemModel->solution.size() > 0 && bemModel->nsol > 0) { + stream->write_float_matrix(FIFF_BEM_POT_SOLUTION, bemModel->solution); } stream->end_block(FIFFB_BEM); @@ -778,6 +771,6 @@ bool SetupForwardModel::prepareBemSolution(const QString& bemFile, printf("Saved the result to %s\n", qPrintable(solFile)); - delete bemModel; + return true; } diff --git a/src/applications/mne_surf2bem/surf2bem.cpp b/src/applications/mne_surf2bem/surf2bem.cpp index c5ec019ddb5..d94e5d8c128 100644 --- a/src/applications/mne_surf2bem/surf2bem.cpp +++ b/src/applications/mne_surf2bem/surf2bem.cpp @@ -45,7 +45,7 @@ #include "mne_surf2bem_settings.h" #include "surfacechecks.h" -#include +#include #include #include @@ -117,6 +117,7 @@ int Surf2Bem::run() if (!m_settings.outputFile().isEmpty()) fprintf(stderr, "output file : %s\n", qPrintable(m_settings.outputFile())); fprintf(stderr, "\n"); + fflush(stderr); // // Step 1: Read all surfaces @@ -136,12 +137,14 @@ int Surf2Bem::run() } if (!ok) { - qCritical() << "Failed to read surface" << inputs[k].fileName; + fprintf(stderr, "Failed to read surface %s\n", qPrintable(inputs[k].fileName)); + fflush(stderr); return 1; } surfs[k].id = inputs[k].id; fprintf(stderr, "%s read. id = %d\n\n", qPrintable(inputs[k].fileName), inputs[k].id); + fflush(stderr); } // @@ -226,7 +229,8 @@ bool Surf2Bem::readFreeSurferSurface(const SurfaceInput& input, MNEBemSurface& b // QFile file(input.fileName); if (!file.open(QIODevice::ReadOnly)) { - qCritical() << "Could not open surface file:" << input.fileName; + fprintf(stderr, "Could not open surface file: %s\n", qPrintable(input.fileName)); + fflush(stderr); return false; } @@ -252,6 +256,7 @@ bool Surf2Bem::readFreeSurferSurface(const SurfaceInput& input, MNEBemSurface& b fprintf(stderr, "%s: triangle file with %d vertices and %d triangles\n", qPrintable(input.fileName), nvert, nface); + fflush(stderr); // Read vertices (stored as 3 x nvert, column-major float32 big-endian) verts.resize(3, nvert); @@ -268,8 +273,9 @@ bool Surf2Bem::readFreeSurferSurface(const SurfaceInput& input, MNEBemSurface& b } } } else { - qCritical() << "Unsupported surface file format (magic =" << magic << ") in" << input.fileName; - qCritical() << "Only FreeSurfer triangle format (0xFFFFFE) is supported for BEM surfaces."; + fprintf(stderr, "Unsupported surface file format (magic = %d) in %s\n", magic, qPrintable(input.fileName)); + fprintf(stderr, "Only FreeSurfer triangle format (0xFFFFFE) is supported for BEM surfaces.\n"); + fflush(stderr); return false; } @@ -293,6 +299,7 @@ bool Surf2Bem::readFreeSurferSurface(const SurfaceInput& input, MNEBemSurface& b fprintf(stderr, "Read FreeSurfer surface: %d vertices, %d triangles\n", bemSurf.np, bemSurf.ntri); + fflush(stderr); return true; } diff --git a/src/applications/mne_watershed_bem/watershedbem.cpp b/src/applications/mne_watershed_bem/watershedbem.cpp index 6a9af90cdc8..10a1aab0ccc 100644 --- a/src/applications/mne_watershed_bem/watershedbem.cpp +++ b/src/applications/mne_watershed_bem/watershedbem.cpp @@ -51,7 +51,7 @@ #include "watershedbem.h" #include "mne_watershed_bem_settings.h" -#include +#include #include #include diff --git a/src/examples/ex_averaging/main.cpp b/src/examples/ex_averaging/main.cpp index 0802b0f233e..3d67e33212c 100644 --- a/src/examples/ex_averaging/main.cpp +++ b/src/examples/ex_averaging/main.cpp @@ -55,6 +55,7 @@ #include #include +#include //============================================================================================================= // USED NAMESPACES diff --git a/src/examples/ex_cancel_noise/main.cpp b/src/examples/ex_cancel_noise/main.cpp index ef75b9a1853..7c51b094fec 100644 --- a/src/examples/ex_cancel_noise/main.cpp +++ b/src/examples/ex_cancel_noise/main.cpp @@ -52,6 +52,7 @@ #include #include +#include //============================================================================================================= // USED NAMESPACES diff --git a/src/examples/ex_clustered_inverse_mne/main.cpp b/src/examples/ex_clustered_inverse_mne/main.cpp index 5e121acbab4..ef63c380247 100644 --- a/src/examples/ex_clustered_inverse_mne/main.cpp +++ b/src/examples/ex_clustered_inverse_mne/main.cpp @@ -38,14 +38,14 @@ // INCLUDES //============================================================================================================= -#include -#include -#include -#include +#include +#include +#include +#include #include -#include -#include +#include +#include #include #include @@ -70,6 +70,7 @@ //============================================================================================================= using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; using namespace FIFFLIB; using namespace INVERSELIB; @@ -157,7 +158,7 @@ int main(int argc, char *argv[]) std::cout << "evoked first " << evoked.first << "; last " << evoked.last << std::endl; - MNEForwardSolution t_Fwd(t_fileFwd); + FwdForwardSolution t_Fwd(t_fileFwd); if(t_Fwd.isEmpty()) return 1; @@ -169,7 +170,7 @@ int main(int argc, char *argv[]) // // Cluster forward solution; // - MNEForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); + FwdForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); // std::cout << "Size " << t_clusteredFwd.sol->data.rows() << " x " << t_clusteredFwd.sol->data.cols() << std::endl; // std::cout << "Clustered Fwd:\n" << t_clusteredFwd.sol->data.row(0) << std::endl; diff --git a/src/examples/ex_clustered_inverse_mne_raw/main.cpp b/src/examples/ex_clustered_inverse_mne_raw/main.cpp index 12dd87a06bf..d7d5e572a54 100644 --- a/src/examples/ex_clustered_inverse_mne_raw/main.cpp +++ b/src/examples/ex_clustered_inverse_mne_raw/main.cpp @@ -39,21 +39,21 @@ // INCLUDES //============================================================================================================= -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include -#include +#include #include -#include +#include #include #include @@ -80,6 +80,7 @@ //============================================================================================================= using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; using namespace FIFFLIB; using namespace INVERSELIB; @@ -476,7 +477,7 @@ int main(int argc, char *argv[]) // // Load data // - MNEForwardSolution t_Fwd(t_fileFwd); + FwdForwardSolution t_Fwd(t_fileFwd); if(t_Fwd.isEmpty()) return 1; @@ -491,7 +492,7 @@ int main(int argc, char *argv[]) // Cluster forward solution; // MatrixXd D; - MNEForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20, D, noise_cov, evoked.info); + FwdForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20, D, noise_cov, evoked.info); // // make an inverse operators diff --git a/src/examples/ex_clustered_inverse_pwl_rap_music_raw/main.cpp b/src/examples/ex_clustered_inverse_pwl_rap_music_raw/main.cpp index 4e9c7d84c82..79dfee25146 100644 --- a/src/examples/ex_clustered_inverse_pwl_rap_music_raw/main.cpp +++ b/src/examples/ex_clustered_inverse_pwl_rap_music_raw/main.cpp @@ -39,19 +39,19 @@ // INCLUDES //============================================================================================================= -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include -#include +#include -#include +#include #include #include @@ -80,6 +80,7 @@ //============================================================================================================= using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; using namespace FIFFLIB; using namespace INVERSELIB; @@ -192,7 +193,7 @@ int main(int argc, char *argv[]) // // Load data // - MNEForwardSolution t_Fwd(t_fileFwd); + FwdForwardSolution t_Fwd(t_fileFwd); if(t_Fwd.isEmpty()) return 1; @@ -477,7 +478,7 @@ int main(int argc, char *argv[]) // // Cluster forward solution; // - MNEForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); + FwdForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); // // Compute inverse solution diff --git a/src/examples/ex_clustered_inverse_rap_music_raw/main.cpp b/src/examples/ex_clustered_inverse_rap_music_raw/main.cpp index f5331a5707d..331c4cea852 100644 --- a/src/examples/ex_clustered_inverse_rap_music_raw/main.cpp +++ b/src/examples/ex_clustered_inverse_rap_music_raw/main.cpp @@ -39,19 +39,19 @@ // INCLUDES //============================================================================================================= -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include -#include +#include -#include +#include #include #include @@ -77,6 +77,7 @@ //============================================================================================================= using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; using namespace FIFFLIB; using namespace INVERSELIB; @@ -189,7 +190,7 @@ int main(int argc, char *argv[]) // // Load data // - MNEForwardSolution t_Fwd(t_fileFwd); + FwdForwardSolution t_Fwd(t_fileFwd); if(t_Fwd.isEmpty()) return 1; @@ -472,7 +473,7 @@ int main(int argc, char *argv[]) // // Cluster forward solution; // - MNEForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); + FwdForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); // // Compute inverse solution diff --git a/src/examples/ex_compute_forward/main.cpp b/src/examples/ex_compute_forward/main.cpp index 2f709a7269f..25339338c95 100644 --- a/src/examples/ex_compute_forward/main.cpp +++ b/src/examples/ex_compute_forward/main.cpp @@ -42,10 +42,10 @@ #include #include -#include -#include +#include +#include -#include +#include #include //============================================================================================================= @@ -58,6 +58,7 @@ #include #include +#include #include //============================================================================================================= @@ -131,37 +132,31 @@ int main(int argc, char *argv[]) pSettings->checkIntegrity(); timer0.start(); - QSharedPointer pComputeFwd = QSharedPointer(new FWDLIB::ComputeFwd(pSettings)); + ComputeFwd::SPtr pFwdComputer = ComputeFwd::SPtr(new ComputeFwd(pSettings)); fTime0 = timer0.elapsed(); // perform the actual computation timer1.start(); - pComputeFwd->calculateFwd(); + auto pFwdSolution = pFwdComputer->calculateFwd(); fTime1 = timer1.elapsed(); - // ToDo: Refactor fwd-lib and make MNEForwardSolution a member of computeForward, - // so next two steps will not be necessary - - // store calculated forward solution in pSettings->solname specified file + // store calculated forward solution timer2.start(); - pComputeFwd->storeFwd(); + QFile fwdFile(pSettings->solname); + pFwdSolution->write(fwdFile); fTime2 = timer2.elapsed(); - // read as MNEForwardSolution + // read back to verify round-trip timer3.start(); QFile t_solution(pSettings->solname); - QSharedPointer pFwdSolution = QSharedPointer(new MNEForwardSolution(t_solution)); + FwdForwardSolution::SPtr pFwdRead = FwdForwardSolution::SPtr(new FwdForwardSolution(t_solution)); fTime3 = timer3.elapsed(); - // update head position to forward solution and only recompute necessary part + // update head position and recompute timer4.start(); - pComputeFwd->updateHeadPos(meg_head_t); + pFwdComputer->updateHeadPos(meg_head_t, *pFwdSolution); fTime4 = timer4.elapsed(); - // get updated solution - pFwdSolution->sol = pComputeFwd->sol; - pFwdSolution->sol_grad = pComputeFwd->sol_grad; - // Print timer results qInfo() << "The initialization took: " << fTime0 << " ms."; qInfo() << "The computation took: " << fTime1 << " ms."; diff --git a/src/examples/ex_connectivity/main.cpp b/src/examples/ex_connectivity/main.cpp index 450ee5d0fc1..af151c1ebe1 100644 --- a/src/examples/ex_connectivity/main.cpp +++ b/src/examples/ex_connectivity/main.cpp @@ -53,15 +53,15 @@ #include -#include -#include -#include +#include +#include +#include -#include +#include #include #include -#include +#include #include #include @@ -98,6 +98,7 @@ using namespace CONNECTIVITYLIB; using namespace Eigen; using namespace UTILSLIB; using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; //============================================================================================================= @@ -205,8 +206,8 @@ int main(int argc, char *argv[]) MatrixXi events; RowVectorXi picks; - MNEForwardSolution t_clusteredFwd; - MNEForwardSolution t_Fwd; + FwdForwardSolution t_clusteredFwd; + FwdForwardSolution t_Fwd; SurfaceSet tSurfSetInflated (sSubj, 2, "inflated", sSubjDir); AnnotationSet tAnnotSet(sSubj, 2, sAnnotType, sSubjDir); @@ -305,7 +306,7 @@ int main(int argc, char *argv[]) } else { //Create source level data QFile t_fileFwd(sFwd); - t_Fwd = MNEForwardSolution(t_fileFwd, false, true); + t_Fwd = FwdForwardSolution(t_fileFwd, false, true); // Load data MNESourceEstimate sourceEstimate; diff --git a/src/examples/ex_connectivity_comparison/main.cpp b/src/examples/ex_connectivity_comparison/main.cpp index 30e22a2e661..108e33734f0 100644 --- a/src/examples/ex_connectivity_comparison/main.cpp +++ b/src/examples/ex_connectivity_comparison/main.cpp @@ -49,15 +49,15 @@ #include -#include -#include -#include +#include +#include +#include -#include +#include #include #include -#include +#include #include #include @@ -88,6 +88,7 @@ using namespace CONNECTIVITYLIB; using namespace Eigen; using namespace UTILSLIB; using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; //============================================================================================================= @@ -199,8 +200,8 @@ int main(int argc, char *argv[]) MatrixXi events; RowVectorXi picks; - MNEForwardSolution t_clusteredFwd; - MNEForwardSolution t_Fwd; + FwdForwardSolution t_clusteredFwd; + FwdForwardSolution t_Fwd; SurfaceSet tSurfSetInflated (sSubj, 2, "inflated", sSubjDir); AnnotationSet tAnnotSet(sSubj, 2, sAnnotType, sSubjDir); @@ -291,7 +292,7 @@ int main(int argc, char *argv[]) } else { //Create source level data QFile t_fileFwd(sFwd); - t_Fwd = MNEForwardSolution(t_fileFwd, false, true); + t_Fwd = FwdForwardSolution(t_fileFwd, false, true); // Load data MNESourceEstimate sourceEstimate; diff --git a/src/examples/ex_disp/main.cpp b/src/examples/ex_disp/main.cpp index 2d47d357fbe..3e2ea6769ec 100644 --- a/src/examples/ex_disp/main.cpp +++ b/src/examples/ex_disp/main.cpp @@ -64,6 +64,7 @@ #include #include #include +#include //============================================================================================================= // USED NAMESPACES diff --git a/src/examples/ex_disp_3D/main.cpp b/src/examples/ex_disp_3D/main.cpp index a9e467a129c..6ec1827f6da 100644 --- a/src/examples/ex_disp_3D/main.cpp +++ b/src/examples/ex_disp_3D/main.cpp @@ -47,15 +47,15 @@ #include #include -#include -#include +#include +#include -#include +#include #include #include -#include +#include #include @@ -80,6 +80,7 @@ //============================================================================================================= using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; using namespace FIFFLIB; using namespace INVERSELIB; @@ -163,8 +164,8 @@ int main(int argc, char *argv[]) AnnotationSet tAnnotSet (parser.value(subjectOption), parser.value(hemiOption).toInt(), parser.value(annotOption), parser.value(subjectPathOption)); QFile t_fileFwd(parser.value(fwdOption)); - MNEForwardSolution t_Fwd(t_fileFwd); - MNEForwardSolution t_clusteredFwd; + FwdForwardSolution t_Fwd(t_fileFwd); + FwdForwardSolution t_clusteredFwd; QString t_sFileClusteredInverse(parser.value(invOpOption)); diff --git a/src/examples/ex_evoked_grad_amp/main.cpp b/src/examples/ex_evoked_grad_amp/main.cpp index 7e7fa081ef9..1b1934f2950 100644 --- a/src/examples/ex_evoked_grad_amp/main.cpp +++ b/src/examples/ex_evoked_grad_amp/main.cpp @@ -54,6 +54,7 @@ #include #include +#include //============================================================================================================= // USED NAMESPACES diff --git a/src/examples/ex_find_evoked/main.cpp b/src/examples/ex_find_evoked/main.cpp index a4566a0810b..f7627c12344 100644 --- a/src/examples/ex_find_evoked/main.cpp +++ b/src/examples/ex_find_evoked/main.cpp @@ -52,6 +52,7 @@ #include #include +#include //============================================================================================================= // USED NAMESPACES diff --git a/src/examples/ex_fs_surface/main.cpp b/src/examples/ex_fs_surface/main.cpp index f34bb0ba50d..f7c767c2b09 100644 --- a/src/examples/ex_fs_surface/main.cpp +++ b/src/examples/ex_fs_surface/main.cpp @@ -40,7 +40,7 @@ #include #include -#include +#include #include diff --git a/src/examples/ex_histogram/main.cpp b/src/examples/ex_histogram/main.cpp index 852afe9e737..fb28f1cf943 100644 --- a/src/examples/ex_histogram/main.cpp +++ b/src/examples/ex_histogram/main.cpp @@ -54,12 +54,12 @@ #include //includes for source localization data -#include -#include -#include +#include +#include +#include #include -#include -#include +#include +#include //============================================================================================================= // QT INCLUDES @@ -75,6 +75,7 @@ //includes for source localization data #include #include +#include #include //============================================================================================================= @@ -83,6 +84,7 @@ using namespace FIFFLIB; using namespace MNELIB; +using namespace FWDLIB; using namespace std; using namespace DISPLIB; using namespace FSLIB; @@ -164,7 +166,7 @@ int main(int argc, char *argv[]) std::cout << "evoked first " << evoked.first << "; last " << evoked.last << std::endl; - MNEForwardSolution t_Fwd(t_fileFwd); + FwdForwardSolution t_Fwd(t_fileFwd); if(t_Fwd.isEmpty()) return 1; @@ -178,7 +180,7 @@ int main(int argc, char *argv[]) // // Cluster forward solution; // - MNEForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); + FwdForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); // std::cout << "Size " << t_clusteredFwd.sol->data.rows() << " x " << t_clusteredFwd.sol->data.cols() << std::endl; // std::cout << "Clustered Fwd:\n" << t_clusteredFwd.sol->data.row(0) << std::endl; diff --git a/src/examples/ex_hpiFit/CMakeLists.txt b/src/examples/ex_hpiFit/CMakeLists.txt index 2ead5daff60..3007d66d56d 100644 --- a/src/examples/ex_hpiFit/CMakeLists.txt +++ b/src/examples/ex_hpiFit/CMakeLists.txt @@ -24,9 +24,9 @@ set(QT_REQUIRED_COMPONENT_LIBS ${QT_REQUIRED_COMPONENTS}) list(TRANSFORM QT_REQUIRED_COMPONENT_LIBS PREPEND "Qt${QT_VERSION_MAJOR}::") set(MNE_LIBS_REQUIRED + mne_inverse mne_rtprocessing mne_connectivity - mne_inverse mne_fwd mne_mne mne_fiff diff --git a/src/examples/ex_hpiFit/main.cpp b/src/examples/ex_hpiFit/main.cpp index 480b4ece776..4a28a3e0727 100644 --- a/src/examples/ex_hpiFit/main.cpp +++ b/src/examples/ex_hpiFit/main.cpp @@ -43,8 +43,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/src/examples/ex_interpolation/main.cpp b/src/examples/ex_interpolation/main.cpp index 39785aa847e..06bbeeec7d6 100644 --- a/src/examples/ex_interpolation/main.cpp +++ b/src/examples/ex_interpolation/main.cpp @@ -41,7 +41,7 @@ // INCLUDES //============================================================================================================= -#include +#include #include #include diff --git a/src/examples/ex_inverse_mne/main.cpp b/src/examples/ex_inverse_mne/main.cpp index a828dcb2911..2871e82030e 100644 --- a/src/examples/ex_inverse_mne/main.cpp +++ b/src/examples/ex_inverse_mne/main.cpp @@ -46,11 +46,11 @@ //============================================================================================================= #include -#include +#include #include -#include -#include +#include +#include #include @@ -60,6 +60,7 @@ #include #include +#include //============================================================================================================= // USED NAMESPACES diff --git a/src/examples/ex_inverse_mne_raw/main.cpp b/src/examples/ex_inverse_mne_raw/main.cpp index 957511ccfde..6ef956e5f29 100644 --- a/src/examples/ex_inverse_mne_raw/main.cpp +++ b/src/examples/ex_inverse_mne_raw/main.cpp @@ -47,21 +47,21 @@ // MNE INCLUDES //============================================================================================================= -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include -#include +#include #include -#include +#include #include #include @@ -84,6 +84,7 @@ //============================================================================================================= using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; using namespace FIFFLIB; using namespace INVERSELIB; @@ -501,7 +502,7 @@ int main(int argc, char *argv[]) // // Load data // - MNEForwardSolution t_Fwd(t_fileFwd); + FwdForwardSolution t_Fwd(t_fileFwd); if(t_Fwd.isEmpty()) return 1; diff --git a/src/examples/ex_inverse_pwl_rap_music/main.cpp b/src/examples/ex_inverse_pwl_rap_music/main.cpp index 53bc518d748..c0bb6664147 100644 --- a/src/examples/ex_inverse_pwl_rap_music/main.cpp +++ b/src/examples/ex_inverse_pwl_rap_music/main.cpp @@ -30,7 +30,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * - * @brief Example of MNEForwardSolution and Powell Rap Music application + * @brief Example of FwdForwardSolution and Powell Rap Music application * */ @@ -38,18 +38,18 @@ // INCLUDES //============================================================================================================= -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include -#include +#include -#include +#include #include #include @@ -73,6 +73,7 @@ //============================================================================================================= using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; using namespace FIFFLIB; using namespace INVERSELIB; @@ -155,7 +156,7 @@ int main(int argc, char *argv[]) std::cout << "evoked first " << evoked.first << "; last " << evoked.last << std::endl; - MNEForwardSolution t_Fwd(t_fileFwd); + FwdForwardSolution t_Fwd(t_fileFwd); if(t_Fwd.isEmpty()) return 1; @@ -165,7 +166,7 @@ int main(int argc, char *argv[]) // // Cluster forward solution; // - MNEForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); + FwdForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); // std::cout << "Size " << t_clusteredFwd.sol->data.rows() << " x " << t_clusteredFwd.sol->data.cols() << std::endl; // std::cout << "Clustered Fwd:\n" << t_clusteredFwd.sol->data.row(0) << std::endl; diff --git a/src/examples/ex_inverse_rap_music/main.cpp b/src/examples/ex_inverse_rap_music/main.cpp index f875e0778e2..4d668783892 100644 --- a/src/examples/ex_inverse_rap_music/main.cpp +++ b/src/examples/ex_inverse_rap_music/main.cpp @@ -30,7 +30,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * - * @brief Example of MNEForwardSolution and RapMusic application + * @brief Example of FwdForwardSolution and RapMusic application * */ @@ -38,18 +38,18 @@ // INCLUDES //============================================================================================================= -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include -#include +#include -#include +#include #include #include @@ -73,6 +73,7 @@ //============================================================================================================= using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; using namespace FIFFLIB; using namespace INVERSELIB; @@ -155,7 +156,7 @@ int main(int argc, char *argv[]) std::cout << "evoked first " << evoked.first << "; last " << evoked.last << std::endl; - MNEForwardSolution t_Fwd(t_fileFwd); + FwdForwardSolution t_Fwd(t_fileFwd); if(t_Fwd.isEmpty()) return 1; @@ -165,7 +166,7 @@ int main(int argc, char *argv[]) // // Cluster forward solution; // - MNEForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); + FwdForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); // std::cout << "Size " << t_clusteredFwd.sol->data.rows() << " x " << t_clusteredFwd.sol->data.cols() << std::endl; // std::cout << "Clustered Fwd:\n" << t_clusteredFwd.sol->data.row(0) << std::endl; diff --git a/src/examples/ex_make_inverse_operator/main.cpp b/src/examples/ex_make_inverse_operator/main.cpp index 094e1ff9209..093ac80488c 100644 --- a/src/examples/ex_make_inverse_operator/main.cpp +++ b/src/examples/ex_make_inverse_operator/main.cpp @@ -39,12 +39,13 @@ #include #include -#include -#include +#include +#include #include #include #include +#include //============================================================================================================= // QT INCLUDES @@ -58,6 +59,7 @@ using namespace FIFFLIB; using namespace MNELIB; +using namespace FWDLIB; using namespace INVERSELIB; using namespace UTILSLIB; @@ -116,7 +118,7 @@ int main(int argc, char *argv[]) if(evoked.isEmpty()) return 1; - MNEForwardSolution t_forwardMeeg(t_fileFwdMeeg, false, true); + FwdForwardSolution t_forwardMeeg(t_fileFwdMeeg, false, true); FiffCov noise_cov(t_fileCov); @@ -124,9 +126,9 @@ int main(int argc, char *argv[]) noise_cov = noise_cov.regularize(evoked.info, 0.05, 0.05, 0.1, true); // Restrict forward solution as necessary for MEG - MNEForwardSolution t_forwardMeg = t_forwardMeeg.pick_types(true, false); + FwdForwardSolution t_forwardMeg = t_forwardMeeg.pick_types(true, false); // Alternatively, you can just load a forward solution that is restricted - MNEForwardSolution t_forwardEeg(t_fileFwdEeg, false, true); + FwdForwardSolution t_forwardEeg(t_fileFwdEeg, false, true); // make an M/EEG, MEG-only, and EEG-only inverse operators FiffInfo info = evoked.info; diff --git a/src/examples/ex_read_epochs/main.cpp b/src/examples/ex_read_epochs/main.cpp index e3d5a4a0fdb..ba7580364d9 100644 --- a/src/examples/ex_read_epochs/main.cpp +++ b/src/examples/ex_read_epochs/main.cpp @@ -58,6 +58,7 @@ #include #include +#include //============================================================================================================= // USED NAMESPACES diff --git a/src/examples/ex_read_evoked/main.cpp b/src/examples/ex_read_evoked/main.cpp index ae4aa199b81..952af41a289 100644 --- a/src/examples/ex_read_evoked/main.cpp +++ b/src/examples/ex_read_evoked/main.cpp @@ -51,6 +51,7 @@ #include #include +#include //============================================================================================================= // USED NAMESPACES diff --git a/src/examples/ex_read_fwd/main.cpp b/src/examples/ex_read_fwd/main.cpp index 8768acf13cc..dd18e0d7821 100644 --- a/src/examples/ex_read_fwd/main.cpp +++ b/src/examples/ex_read_fwd/main.cpp @@ -38,7 +38,8 @@ //============================================================================================================= #include -#include +#include +#include #include #include @@ -54,7 +55,7 @@ // USED NAMESPACES //============================================================================================================= -using namespace MNELIB; +using namespace FWDLIB; using namespace UTILSLIB; using namespace FSLIB; @@ -99,7 +100,7 @@ int main(int argc, char *argv[]) //Load data QFile t_fileForwardSolution(parser.value(fwdFileOption)); - MNEForwardSolution t_Fwd(t_fileForwardSolution); + FwdForwardSolution t_Fwd(t_fileForwardSolution); if(t_Fwd.source_ori != -1) { @@ -114,7 +115,7 @@ int main(int argc, char *argv[]) // // Cluster forward solution; // - MNEForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); + FwdForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); qDebug() << "==== Results ===="; qDebug() << "nrow: " << t_clusteredFwd.sol->nrow; diff --git a/src/examples/ex_read_fwd_disp_3D/main.cpp b/src/examples/ex_read_fwd_disp_3D/main.cpp index d1611995295..d878eb4f571 100644 --- a/src/examples/ex_read_fwd_disp_3D/main.cpp +++ b/src/examples/ex_read_fwd_disp_3D/main.cpp @@ -40,9 +40,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include //============================================================================================================= @@ -51,6 +51,7 @@ #include #include +#include #include //============================================================================================================= @@ -58,6 +59,7 @@ //============================================================================================================= using namespace MNELIB; +using namespace FWDLIB; using namespace MNELIB; using namespace UTILSLIB; using namespace FSLIB; @@ -107,7 +109,7 @@ int main(int argc, char *argv[]) //Load data QFile t_File(parser.value(fwdFileOption)); - MNEForwardSolution t_forwardSolution(t_File); + FwdForwardSolution t_forwardSolution(t_File); BrainView *pBrainView = new BrainView(); BrainTreeModel *pModel = new BrainTreeModel(); diff --git a/src/examples/ex_read_raw/main.cpp b/src/examples/ex_read_raw/main.cpp index 7bcd874eddd..d3a3224abf5 100644 --- a/src/examples/ex_read_raw/main.cpp +++ b/src/examples/ex_read_raw/main.cpp @@ -51,6 +51,7 @@ #include #include +#include //============================================================================================================= // USED NAMESPACES diff --git a/src/examples/ex_roi_clustered_inverse_pwl_rap_music/main.cpp b/src/examples/ex_roi_clustered_inverse_pwl_rap_music/main.cpp index a6c94c1cacd..82c63ad6007 100644 --- a/src/examples/ex_roi_clustered_inverse_pwl_rap_music/main.cpp +++ b/src/examples/ex_roi_clustered_inverse_pwl_rap_music/main.cpp @@ -38,16 +38,16 @@ // INCLUDES //============================================================================================================= -#include -#include -#include +#include +#include +#include #include #include #include -#include -#include +#include +#include #include #include @@ -71,6 +71,7 @@ //============================================================================================================= using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; using namespace FIFFLIB; using namespace INVERSELIB; @@ -162,7 +163,7 @@ int main(int argc, char *argv[]) // // Read raw data // - MNEForwardSolution t_Fwd(t_fileFwd); + FwdForwardSolution t_Fwd(t_fileFwd); if(t_Fwd.isEmpty()) return 1; @@ -187,7 +188,7 @@ int main(int argc, char *argv[]) // std::cout << t_qListLabels[i].hemi << std::endl; // } - MNEForwardSolution t_SelectFwd = t_Fwd.pick_regions(t_qListLabelSelection); + FwdForwardSolution t_SelectFwd = t_Fwd.pick_regions(t_qListLabelSelection); // // Setup for reading the raw data @@ -286,7 +287,7 @@ int main(int argc, char *argv[]) // // Cluster forward solution; // - MNEForwardSolution t_clusteredFwd = t_SelectFwd.cluster_forward_solution(t_annotationSet, 20);//t_Fwd.cluster_forward_solution_ccr(t_annotationSet, 20);//40); + FwdForwardSolution t_clusteredFwd = t_SelectFwd.cluster_forward_solution(t_annotationSet, 20);//t_Fwd.cluster_forward_solution_ccr(t_annotationSet, 20);//40); // // Compute inverse solution diff --git a/src/examples/ex_st_clustered_inverse_pwl_rap_music/main.cpp b/src/examples/ex_st_clustered_inverse_pwl_rap_music/main.cpp index b76ea87f4d7..8553771f6f1 100644 --- a/src/examples/ex_st_clustered_inverse_pwl_rap_music/main.cpp +++ b/src/examples/ex_st_clustered_inverse_pwl_rap_music/main.cpp @@ -38,10 +38,10 @@ // INCLUDES //============================================================================================================= -#include -#include -#include -#include +#include +#include +#include +#include #include #include @@ -49,8 +49,8 @@ #include -#include -#include +#include +#include #include #include @@ -74,6 +74,7 @@ //============================================================================================================= using namespace MNELIB; +using namespace FWDLIB; using namespace FSLIB; using namespace FIFFLIB; using namespace INVERSELIB; @@ -166,7 +167,7 @@ int main(int argc, char *argv[]) // // Read raw data // - MNEForwardSolution t_Fwd(t_fileFwd); + FwdForwardSolution t_Fwd(t_fileFwd); if(t_Fwd.isEmpty()) return 1; @@ -267,7 +268,7 @@ int main(int argc, char *argv[]) // // Cluster forward solution; // - MNEForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); + FwdForwardSolution t_clusteredFwd = t_Fwd.cluster_forward_solution(t_annotationSet, 20);//40); // // Compute inverse solution diff --git a/src/libraries/connectivity/CMakeLists.txt b/src/libraries/connectivity/CMakeLists.txt index b3826002099..4864bc00746 100644 --- a/src/libraries/connectivity/CMakeLists.txt +++ b/src/libraries/connectivity/CMakeLists.txt @@ -84,6 +84,7 @@ set(MNE_LIBS_REQUIRED mne_fiff mne_fs mne_mne + mne_fwd ) target_link_libraries(${PROJECT_NAME} PRIVATE diff --git a/src/libraries/connectivity/connectivitysettings.cpp b/src/libraries/connectivity/connectivitysettings.cpp index 7a2a75515b4..c4c51134c02 100644 --- a/src/libraries/connectivity/connectivitysettings.cpp +++ b/src/libraries/connectivity/connectivitysettings.cpp @@ -38,8 +38,8 @@ #include "connectivitysettings.h" -#include -#include +#include +#include #include //============================================================================================================= @@ -60,6 +60,7 @@ using namespace CONNECTIVITYLIB; using namespace MNELIB; +using namespace FWDLIB; using namespace Eigen; using namespace FIFFLIB; using namespace FSLIB; @@ -358,7 +359,7 @@ void ConnectivitySettings::setNodePositions(const FiffInfo& fiffInfo, //******************************************************************************************************* -void ConnectivitySettings::setNodePositions(const MNEForwardSolution& forwardSolution, const SurfaceSet& surfSet) +void ConnectivitySettings::setNodePositions(const FwdForwardSolution& forwardSolution, const SurfaceSet& surfSet) { //Generate node vertices MatrixX3f matNodeVertLeft, matNodeVertRight; diff --git a/src/libraries/connectivity/connectivitysettings.h b/src/libraries/connectivity/connectivitysettings.h index 3dfa41b01af..03a708452d6 100644 --- a/src/libraries/connectivity/connectivitysettings.h +++ b/src/libraries/connectivity/connectivitysettings.h @@ -60,8 +60,8 @@ // FORWARD DECLARATIONS //============================================================================================================= -namespace MNELIB { - class MNEForwardSolution; +namespace FWDLIB { + class FwdForwardSolution; } namespace FSLIB { @@ -166,7 +166,7 @@ class CONNECTIVITYSHARED_EXPORT ConnectivitySettings void setNodePositions(const FIFFLIB::FiffInfo& fiffInfo, const Eigen::RowVectorXi& picks); - void setNodePositions(const MNELIB::MNEForwardSolution& forwardSolution, + void setNodePositions(const FWDLIB::FwdForwardSolution& forwardSolution, const FSLIB::SurfaceSet& surfSet); void setNodePositions(const Eigen::MatrixX3f& matNodePositions); diff --git a/src/libraries/disp/viewers/fwdsettingsview.cpp b/src/libraries/disp/viewers/fwdsettingsview.cpp index 978673cade0..e16dfa29036 100644 --- a/src/libraries/disp/viewers/fwdsettingsview.cpp +++ b/src/libraries/disp/viewers/fwdsettingsview.cpp @@ -40,7 +40,7 @@ #include "QtWidgets/qspinbox.h" #include "ui_fwdsettingsview.h" -#include +#include //============================================================================================================= // QT INCLUDES diff --git a/src/libraries/disp/viewers/fwdsettingsview.h b/src/libraries/disp/viewers/fwdsettingsview.h index ee574fd7ded..efe4cf0e0dd 100644 --- a/src/libraries/disp/viewers/fwdsettingsview.h +++ b/src/libraries/disp/viewers/fwdsettingsview.h @@ -43,7 +43,7 @@ #include "abstractview.h" #include -#include +#include //============================================================================================================= // QT INCLUDES diff --git a/src/libraries/disp3D_rhi/core/dataloader.cpp b/src/libraries/disp3D_rhi/core/dataloader.cpp index d0fb215e29a..b8c12d6fd03 100644 --- a/src/libraries/disp3D_rhi/core/dataloader.cpp +++ b/src/libraries/disp3D_rhi/core/dataloader.cpp @@ -51,7 +51,7 @@ #include #include #include -#include +#include using namespace FIFFLIB; using namespace MNELIB; diff --git a/src/libraries/disp3D_rhi/core/dataloader.h b/src/libraries/disp3D_rhi/core/dataloader.h index b3c6f614c5f..37a61c0057e 100644 --- a/src/libraries/disp3D_rhi/core/dataloader.h +++ b/src/libraries/disp3D_rhi/core/dataloader.h @@ -53,7 +53,7 @@ #include #include #include -#include +#include //============================================================================================================= // FORWARD DECLARATIONS diff --git a/src/libraries/disp3D_rhi/model/braintreemodel.cpp b/src/libraries/disp3D_rhi/model/braintreemodel.cpp index 3184ff81ce5..ca9efbf10b5 100644 --- a/src/libraries/disp3D_rhi/model/braintreemodel.cpp +++ b/src/libraries/disp3D_rhi/model/braintreemodel.cpp @@ -48,7 +48,7 @@ #include "model/items/sourcespacetreeitem.h" #include "model/items/digitizersettreeitem.h" #include "model/items/networktreeitem.h" -#include +#include #include BrainTreeModel::BrainTreeModel(QObject *parent) diff --git a/src/libraries/disp3D_rhi/model/braintreemodel.h b/src/libraries/disp3D_rhi/model/braintreemodel.h index ff89cd5d813..7cd49d8260c 100644 --- a/src/libraries/disp3D_rhi/model/braintreemodel.h +++ b/src/libraries/disp3D_rhi/model/braintreemodel.h @@ -45,8 +45,8 @@ #include "../disp3D_rhi_global.h" #include -#include -#include +#include +#include #include #include #include diff --git a/src/libraries/disp3D_rhi/model/items/dipoletreeitem.h b/src/libraries/disp3D_rhi/model/items/dipoletreeitem.h index 78b7a4bf8d0..e3e5b94f535 100644 --- a/src/libraries/disp3D_rhi/model/items/dipoletreeitem.h +++ b/src/libraries/disp3D_rhi/model/items/dipoletreeitem.h @@ -43,7 +43,7 @@ #include "../../disp3D_rhi_global.h" #include "abstracttreeitem.h" -#include +#include /** * @brief Tree item representing a set of fitted dipoles in the 3-D scene hierarchy. diff --git a/src/libraries/disp3D_rhi/model/items/surfacetreeitem.h b/src/libraries/disp3D_rhi/model/items/surfacetreeitem.h index 8a1cdf63eac..6fd98fa8f48 100644 --- a/src/libraries/disp3D_rhi/model/items/surfacetreeitem.h +++ b/src/libraries/disp3D_rhi/model/items/surfacetreeitem.h @@ -43,8 +43,8 @@ #include "../../disp3D_rhi_global.h" #include "abstracttreeitem.h" -#include -#include +#include +#include /** * @brief Tree item representing a FreeSurfer cortical surface in the 3-D scene hierarchy. diff --git a/src/libraries/disp3D_rhi/renderable/brainsurface.h b/src/libraries/disp3D_rhi/renderable/brainsurface.h index 67adb25a30c..8987a3ef7df 100644 --- a/src/libraries/disp3D_rhi/renderable/brainsurface.h +++ b/src/libraries/disp3D_rhi/renderable/brainsurface.h @@ -48,8 +48,8 @@ #include #include #include -#include -#include +#include +#include #include #include diff --git a/src/libraries/disp3D_rhi/renderable/dipoleobject.h b/src/libraries/disp3D_rhi/renderable/dipoleobject.h index 54b8e1acdf1..a5a6d1f2025 100644 --- a/src/libraries/disp3D_rhi/renderable/dipoleobject.h +++ b/src/libraries/disp3D_rhi/renderable/dipoleobject.h @@ -45,7 +45,7 @@ #include #include #include -#include +#include // Forward-declare QRhi types so that this header stays QRhi-free class QRhi; diff --git a/src/libraries/disp3D_rhi/renderable/sourceestimateoverlay.cpp b/src/libraries/disp3D_rhi/renderable/sourceestimateoverlay.cpp index 62e7c7e6012..5061e531cba 100644 --- a/src/libraries/disp3D_rhi/renderable/sourceestimateoverlay.cpp +++ b/src/libraries/disp3D_rhi/renderable/sourceestimateoverlay.cpp @@ -69,8 +69,8 @@ bool SourceEstimateOverlay::loadStc(const QString &path, int hemi) QFile file(path); // Note: MNESourceEstimate::read() opens the file internally, don't open it here - MNELIB::MNESourceEstimate stc; - if (!MNELIB::MNESourceEstimate::read(file, stc)) { + INVERSELIB::MNESourceEstimate stc; + if (!INVERSELIB::MNESourceEstimate::read(file, stc)) { qWarning() << "SourceEstimateOverlay::loadStc - Failed to read STC file:" << path; return false; } @@ -114,7 +114,7 @@ void SourceEstimateOverlay::applyToSurface(BrainSurface *surface, int timeIndex) if (!surface) return; int hemi = surface->hemi(); - const MNELIB::MNESourceEstimate *stc = nullptr; + const INVERSELIB::MNESourceEstimate *stc = nullptr; QSharedPointer> interpMat; if (hemi == 0 && m_hasLh) { @@ -292,7 +292,7 @@ void SourceEstimateOverlay::computeInterpolationMatrix(BrainSurface *surface, in { if (!surface) return; - const MNELIB::MNESourceEstimate *stc = nullptr; + const INVERSELIB::MNESourceEstimate *stc = nullptr; QSharedPointer> *pMatPtr = nullptr; if (hemi == 0 && m_hasLh) { @@ -359,7 +359,7 @@ void SourceEstimateOverlay::computeInterpolationMatrix(BrainSurface *surface, in //============================================================================================================= -void SourceEstimateOverlay::setStcData(const MNELIB::MNESourceEstimate &stc, int hemi) +void SourceEstimateOverlay::setStcData(const INVERSELIB::MNESourceEstimate &stc, int hemi) { if (hemi == 0) { m_stcLh = stc; diff --git a/src/libraries/disp3D_rhi/renderable/sourceestimateoverlay.h b/src/libraries/disp3D_rhi/renderable/sourceestimateoverlay.h index 608e9a26c8b..6142cb628ab 100644 --- a/src/libraries/disp3D_rhi/renderable/sourceestimateoverlay.h +++ b/src/libraries/disp3D_rhi/renderable/sourceestimateoverlay.h @@ -41,7 +41,7 @@ #include "../disp3D_rhi_global.h" -#include +#include #include #include #include @@ -197,7 +197,7 @@ class DISP3DRHISHARED_EXPORT SourceEstimateOverlay * @param[in] stc The source estimate data. * @param[in] hemi Hemisphere index (0=lh, 1=rh). */ - void setStcData(const MNELIB::MNESourceEstimate &stc, int hemi); + void setStcData(const INVERSELIB::MNESourceEstimate &stc, int hemi); //========================================================================================================= /** @@ -251,8 +251,8 @@ class DISP3DRHISHARED_EXPORT SourceEstimateOverlay */ uint32_t valueToColor(double value, uint8_t alpha = 255) const; - MNELIB::MNESourceEstimate m_stcLh; /**< Left hemisphere source estimate. */ - MNELIB::MNESourceEstimate m_stcRh; /**< Right hemisphere source estimate. */ + INVERSELIB::MNESourceEstimate m_stcLh; /**< Left hemisphere source estimate. */ + INVERSELIB::MNESourceEstimate m_stcRh; /**< Right hemisphere source estimate. */ bool m_hasLh = false; /**< Flag indicating LH data loaded. */ bool m_hasRh = false; /**< Flag indicating RH data loaded. */ diff --git a/src/libraries/disp3D_rhi/scene/sensorfieldmapper.cpp b/src/libraries/disp3D_rhi/scene/sensorfieldmapper.cpp index d5b8d0c7525..6e124c53fe8 100644 --- a/src/libraries/disp3D_rhi/scene/sensorfieldmapper.cpp +++ b/src/libraries/disp3D_rhi/scene/sensorfieldmapper.cpp @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include @@ -334,7 +334,7 @@ bool SensorFieldMapper::buildMapping( std::unique_ptr coils(templates->create_meg_coils( megChs, megChs.size(), FWD_COIL_ACCURACY_NORMAL, devToTarget)); - if (coils && coils->ncoil > 0) { + if (coils && coils->ncoil() > 0) { m_megMapping = FWDLIB::FwdFieldMap::computeMegMapping( *coils, verts, norms, origin, m_evoked.info, megChNames, @@ -359,7 +359,7 @@ bool SensorFieldMapper::buildMapping( FWDLIB::FwdCoilSet::create_eeg_els( eegChs, eegChs.size(), headMri)); - if (eegCoils && eegCoils->ncoil > 0) { + if (eegCoils && eegCoils->ncoil() > 0) { m_eegMapping = FWDLIB::FwdFieldMap::computeEegMapping( *eegCoils, verts, origin, m_evoked.info, eegChNames, diff --git a/src/libraries/disp3D_rhi/workers/rtsensorinterpolationmatworker.cpp b/src/libraries/disp3D_rhi/workers/rtsensorinterpolationmatworker.cpp index 7559737b602..158d7bea3ab 100644 --- a/src/libraries/disp3D_rhi/workers/rtsensorinterpolationmatworker.cpp +++ b/src/libraries/disp3D_rhi/workers/rtsensorinterpolationmatworker.cpp @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include @@ -285,7 +285,7 @@ void RtSensorInterpolationMatWorker::computeMapping() std::unique_ptr coils(templates->create_meg_coils( megChs, megChs.size(), FWD_COIL_ACCURACY_NORMAL, devToTarget)); - if (coils && coils->ncoil > 0) { + if (coils && coils->ncoil() > 0) { auto mat = FWDLIB::FwdFieldMap::computeMegMapping( *coils, megVerts, norms, origin, kIntrad, kMegMiss); @@ -309,7 +309,7 @@ void RtSensorInterpolationMatWorker::computeMapping() FWDLIB::FwdCoilSet::create_eeg_els( eegChs, eegChs.size(), headMri)); - if (eegCoils && eegCoils->ncoil > 0) { + if (eegCoils && eegCoils->ncoil() > 0) { auto mat = FWDLIB::FwdFieldMap::computeEegMapping( *eegCoils, eegVerts, origin, kIntrad, kEegMiss); diff --git a/src/libraries/disp3D_rhi/workers/rtsourcedatacontroller.cpp b/src/libraries/disp3D_rhi/workers/rtsourcedatacontroller.cpp index 30aa5d0709f..c89bba13b51 100644 --- a/src/libraries/disp3D_rhi/workers/rtsourcedatacontroller.cpp +++ b/src/libraries/disp3D_rhi/workers/rtsourcedatacontroller.cpp @@ -40,7 +40,7 @@ #include "rtsourcedataworker.h" #include "rtsourceinterpolationmatworker.h" -#include +#include #include #include diff --git a/src/libraries/disp3D_rhi/workers/rtsourceinterpolationmatworker.cpp b/src/libraries/disp3D_rhi/workers/rtsourceinterpolationmatworker.cpp index 24776a5c5e0..0d986849409 100644 --- a/src/libraries/disp3D_rhi/workers/rtsourceinterpolationmatworker.cpp +++ b/src/libraries/disp3D_rhi/workers/rtsourceinterpolationmatworker.cpp @@ -40,7 +40,7 @@ #include "helpers/interpolation.h" #include "helpers/geometryinfo.h" -#include +#include #include diff --git a/src/libraries/disp3D_rhi/workers/stcloadingworker.cpp b/src/libraries/disp3D_rhi/workers/stcloadingworker.cpp index 3579efc4535..73493dffa2a 100644 --- a/src/libraries/disp3D_rhi/workers/stcloadingworker.cpp +++ b/src/libraries/disp3D_rhi/workers/stcloadingworker.cpp @@ -79,8 +79,8 @@ void StcLoadingWorker::process() // Load LH STC file if (!m_lhPath.isEmpty()) { QFile file(m_lhPath); - MNELIB::MNESourceEstimate stc; - if (MNELIB::MNESourceEstimate::read(file, stc)) { + INVERSELIB::MNESourceEstimate stc; + if (INVERSELIB::MNESourceEstimate::read(file, stc)) { m_stcLh = stc; m_hasLh = true; qDebug() << "StcLoadingWorker: Loaded LH with" << stc.data.rows() << "vertices," @@ -95,8 +95,8 @@ void StcLoadingWorker::process() // Load RH STC file if (!m_rhPath.isEmpty()) { QFile file(m_rhPath); - MNELIB::MNESourceEstimate stc; - if (MNELIB::MNESourceEstimate::read(file, stc)) { + INVERSELIB::MNESourceEstimate stc; + if (INVERSELIB::MNESourceEstimate::read(file, stc)) { m_stcRh = stc; m_hasRh = true; qDebug() << "StcLoadingWorker: Loaded RH with" << stc.data.rows() << "vertices," diff --git a/src/libraries/disp3D_rhi/workers/stcloadingworker.h b/src/libraries/disp3D_rhi/workers/stcloadingworker.h index 31918399ee7..deb149d3b03 100644 --- a/src/libraries/disp3D_rhi/workers/stcloadingworker.h +++ b/src/libraries/disp3D_rhi/workers/stcloadingworker.h @@ -41,7 +41,7 @@ #include "../disp3D_rhi_global.h" -#include +#include #include #include #include @@ -95,7 +95,7 @@ class DISP3DRHISHARED_EXPORT StcLoadingWorker : public QObject * * @return The left hemisphere source estimate. */ - const MNELIB::MNESourceEstimate& stcLh() const { return m_stcLh; } + const INVERSELIB::MNESourceEstimate& stcLh() const { return m_stcLh; } //========================================================================================================= /** @@ -103,7 +103,7 @@ class DISP3DRHISHARED_EXPORT StcLoadingWorker : public QObject * * @return The right hemisphere source estimate. */ - const MNELIB::MNESourceEstimate& stcRh() const { return m_stcRh; } + const INVERSELIB::MNESourceEstimate& stcRh() const { return m_stcRh; } //========================================================================================================= /** @@ -178,8 +178,8 @@ public slots: BrainSurface *m_rhSurface; /**< Pointer to RH surface. */ double m_cancelDist; /**< Cancel distance for interpolation. */ - MNELIB::MNESourceEstimate m_stcLh; /**< Loaded LH source estimate. */ - MNELIB::MNESourceEstimate m_stcRh; /**< Loaded RH source estimate. */ + INVERSELIB::MNESourceEstimate m_stcLh; /**< Loaded LH source estimate. */ + INVERSELIB::MNESourceEstimate m_stcRh; /**< Loaded RH source estimate. */ bool m_hasLh = false; /**< Flag indicating LH data loaded. */ bool m_hasRh = false; /**< Flag indicating RH data loaded. */ diff --git a/src/libraries/fiff/fiff_ch_info.cpp b/src/libraries/fiff/fiff_ch_info.cpp index 1262a3b392f..ee6f04e9052 100644 --- a/src/libraries/fiff/fiff_ch_info.cpp +++ b/src/libraries/fiff/fiff_ch_info.cpp @@ -39,6 +39,7 @@ //============================================================================================================= #include "fiff_ch_info.h" +#include "fiff_constants.h" //============================================================================================================= // USED NAMESPACES @@ -91,3 +92,19 @@ FiffChInfo::~FiffChInfo() { } +//============================================================================================================= + +bool FiffChInfo::checkEegLocations(const QList& chs, int nch) +{ + const float close = 0.02f; + for (int k = 0; k < nch; k++) { + if (chs.at(k).kind == FIFFV_EEG_CH) { + if (chs.at(k).chpos.r0.norm() < close) { + qCritical("Some EEG channels do not have locations assigned."); + return false; + } + } + } + return true; +} + diff --git a/src/libraries/fiff/fiff_ch_info.h b/src/libraries/fiff/fiff_ch_info.h index 0952b196600..4392626b2cb 100644 --- a/src/libraries/fiff/fiff_ch_info.h +++ b/src/libraries/fiff/fiff_ch_info.h @@ -105,6 +105,17 @@ class FIFFSHARED_EXPORT FiffChInfo */ inline static qint32 storageSize(); + //========================================================================================================= + /** + * Check that all EEG channels in the list have reasonable (non-origin) locations. + * Formerly mne_check_chinfo (MNE C). + * + * @param[in] chs The list of channel info structures to check. + * @param[in] nch The number of channels to check. + * @return true if all EEG channels have valid locations, false otherwise. + */ + static bool checkEegLocations(const QList& chs, int nch); + //========================================================================================================= /** * Overloaded == operator to compare an object to this instance. diff --git a/src/libraries/fiff/fiff_events.cpp b/src/libraries/fiff/fiff_events.cpp index 8b291453133..78fc23de1b6 100644 --- a/src/libraries/fiff/fiff_events.cpp +++ b/src/libraries/fiff/fiff_events.cpp @@ -220,20 +220,33 @@ bool FiffEvents::read_from_ascii(QIODevice &p_IODevice, } QTextStream textStream(&p_IODevice); - QList simpleList; + QList sampleList; + QList beforeList; + QList afterList; while(!textStream.atEnd()){ - int iSample; - textStream >> iSample; - simpleList.append(iSample); - textStream.readLine(); - qDebug() << "Added event:" << iSample; + QString line = textStream.readLine().trimmed(); + if (line.isEmpty()) + continue; + QTextStream lineStream(&line); + int iSample = 0, iBefore = 0, iAfter = 0; + lineStream >> iSample; + if (!lineStream.atEnd()) + lineStream >> iBefore; + if (!lineStream.atEnd()) + lineStream >> iAfter; + sampleList.append(iSample); + beforeList.append(iBefore); + afterList.append(iAfter); + qDebug() << "Added event:" << iSample << iBefore << iAfter; } - p_Events.events.resize(simpleList.size(), 1); + p_Events.events.resize(sampleList.size(), 3); - for(int i = 0; i < simpleList.size(); i++){ - p_Events.events(i,0) = simpleList[i]; + for(int i = 0; i < sampleList.size(); i++){ + p_Events.events(i,0) = sampleList[i]; + p_Events.events(i,1) = beforeList[i]; + p_Events.events(i,2) = afterList[i]; } return true; } diff --git a/src/libraries/fiff/fiff_evoked_set.cpp b/src/libraries/fiff/fiff_evoked_set.cpp index aa79acc6474..c9424e7cd11 100644 --- a/src/libraries/fiff/fiff_evoked_set.cpp +++ b/src/libraries/fiff/fiff_evoked_set.cpp @@ -120,7 +120,17 @@ FiffEvokedSet FiffEvokedSet::pick_channels(const QStringList& include, const QStringList& exclude) const { FiffEvokedSet res; - res.info = this->info; + + // + // Update info to match the channel selection + // + RowVectorXi sel = FiffInfo::pick_channels(this->info.ch_names, include, exclude); + if (sel.cols() > 0) { + res.info = this->info.pick_info(sel); + } else { + res.info = this->info; + } + QList::ConstIterator ev; for(ev = evoked.begin(); ev != evoked.end(); ++ev) res.evoked.push_back(ev->pick_channels(include, exclude)); diff --git a/src/libraries/fiff/fiff_info_base.cpp b/src/libraries/fiff/fiff_info_base.cpp index 28d9d37a600..53991a01dac 100644 --- a/src/libraries/fiff/fiff_info_base.cpp +++ b/src/libraries/fiff/fiff_info_base.cpp @@ -244,6 +244,33 @@ FiffInfoBase FiffInfoBase::pick_info(const RowVectorXi* sel) const } //============================================================================================================= +void FiffInfoBase::mne_read_meg_comp_eeg_ch_info(QList& megp, + int& nmegp, + QList& meg_compp, + int& nmeg_compp, + QList& eegp, + int& neegp, + FiffCoordTrans& meg_head_t, + FiffId& idp) const +{ + for (int k = 0; k < nchan; k++) { + if (chs[k].kind == FIFFV_MEG_CH) { + megp.append(chs[k]); + nmegp++; + } else if (chs[k].kind == FIFFV_REF_MEG_CH) { + meg_compp.append(chs[k]); + nmeg_compp++; + } else if (chs[k].kind == FIFFV_EEG_CH) { + eegp.append(chs[k]); + neegp++; + } + } + meg_head_t = dev_head_t; + idp = meas_id; +} + +//============================================================================================================= + QStringList FiffInfoBase::get_channel_types() { QStringList lChannelTypes; diff --git a/src/libraries/fiff/fiff_info_base.h b/src/libraries/fiff/fiff_info_base.h index 7b67ba79348..4932641b0b1 100644 --- a/src/libraries/fiff/fiff_info_base.h +++ b/src/libraries/fiff/fiff_info_base.h @@ -200,6 +200,30 @@ class FIFFSHARED_EXPORT FiffInfoBase */ friend bool operator== (const FiffInfoBase &a, const FiffInfoBase &b); + /** + * Read MEG, compensation, and EEG channel information from this measurement info. + * + * Classifies channels by kind (FIFFV_MEG_CH, FIFFV_REF_MEG_CH, FIFFV_EEG_CH) + * and returns the device-to-head transform and measurement ID. + * + * @param[out] megp List of MEG channel info descriptors. + * @param[out] nmegp Number of MEG channels found. + * @param[out] meg_compp List of MEG compensation (reference) channel info descriptors. + * @param[out] nmeg_compp Number of compensation channels found. + * @param[out] eegp List of EEG channel info descriptors. + * @param[out] neegp Number of EEG channels found. + * @param[out] meg_head_t Device-to-head coordinate transformation. + * @param[out] idp Measurement ID. + */ + void mne_read_meg_comp_eeg_ch_info(QList& megp, + int& nmegp, + QList& meg_compp, + int& nmeg_compp, + QList& eegp, + int& neegp, + FiffCoordTrans& meg_head_t, + FiffId& idp) const; + /** * Parses the channel info information and returns a string list of channel types. * diff --git a/src/libraries/fiff/fiff_io.cpp b/src/libraries/fiff/fiff_io.cpp index 74ec126a12b..db1dd4732dc 100644 --- a/src/libraries/fiff/fiff_io.cpp +++ b/src/libraries/fiff/fiff_io.cpp @@ -172,10 +172,10 @@ bool FiffIO::read(QIODevice& pIODevice) // //forward solutions // if(hasFwds) { -// MNEForwardSolution p_forwardSolution(pIODevice); +// FwdForwardSolution p_forwardSolution(pIODevice); // //append to corresponding member qlist -// m_qlistFwd.append(QSharedPointer(&p_forwardSolution)); +// m_qlistFwd.append(QSharedPointer(&p_forwardSolution)); // } //print summary diff --git a/src/libraries/fiff/fiff_io.h b/src/libraries/fiff/fiff_io.h index 8df2a537982..36c20e54eec 100644 --- a/src/libraries/fiff/fiff_io.h +++ b/src/libraries/fiff/fiff_io.h @@ -223,7 +223,7 @@ class FIFFSHARED_EXPORT FiffIO : public QObject public: QList > m_qlistRaw; /**< List of raw data sets. */ QList > m_qlistEvoked; /**< List of evoked data sets. */ -// QList > m_qlistFwd; +// QList > m_qlistFwd; //FiffCov, MNEInverseOperator, AnnotationSet, }; diff --git a/src/libraries/fiff/fiff_sparse_matrix.cpp b/src/libraries/fiff/fiff_sparse_matrix.cpp index fb606821ac2..f5319e12aff 100644 --- a/src/libraries/fiff/fiff_sparse_matrix.cpp +++ b/src/libraries/fiff/fiff_sparse_matrix.cpp @@ -414,3 +414,49 @@ FiffSparseMatrix FiffSparseMatrix::fromEigenSparse(const Eigen::SparseMatrix nnz_vec(m); + std::vector> colindex(m); + std::vector> vals(m); + + for (int i = 0; i < m; i++) { + int count = ptrs[i+1] - ptrs[i]; + if (count > 0) { + colindex[i].resize(count); + vals[i].resize(count); + int k = 0; + for (int j = ptrs[i]; j < ptrs[i+1]; j++) { + if (inds[j] <= i) { + vals[i][k] = data[j]; + colindex[i][k] = inds[j]; + k++; + } + } + nnz_vec[i] = k; + } else { + nnz_vec[i] = 0; + } + } + + std::vector ci_ptrs(m); + std::vector val_ptrs(m); + for (int i = 0; i < m; i++) { + ci_ptrs[i] = colindex[i].empty() ? nullptr : colindex[i].data(); + val_ptrs[i] = vals[i].empty() ? nullptr : vals[i].data(); + } + + return create_sparse_rcs(m, n, nnz_vec.data(), ci_ptrs.data(), val_ptrs.data()); +} diff --git a/src/libraries/fiff/fiff_sparse_matrix.h b/src/libraries/fiff/fiff_sparse_matrix.h index 80d154e72b4..da2e3f34f7c 100644 --- a/src/libraries/fiff/fiff_sparse_matrix.h +++ b/src/libraries/fiff/fiff_sparse_matrix.h @@ -167,6 +167,13 @@ class FIFFSHARED_EXPORT FiffSparseMatrix */ FiffSparseMatrix::UPtr mne_add_upper_triangle_rcs(); + /** + * Extract only the lower triangle (including diagonal) from a square RCS matrix. + * + * @return A unique pointer to the newly constructed FiffSparseMatrix with lower-triangle elements. + */ + FiffSparseMatrix::UPtr pickLowerTriangleRcs() const; + /** * Check whether this sparse matrix is empty (no non-zero elements). * diff --git a/src/libraries/fiff/fiff_stream.cpp b/src/libraries/fiff/fiff_stream.cpp index afea2c302bc..4dcfaf94343 100644 --- a/src/libraries/fiff/fiff_stream.cpp +++ b/src/libraries/fiff/fiff_stream.cpp @@ -425,6 +425,132 @@ QStringList FiffStream::read_bad_channels(const FiffDirNode::SPtr& p_Node) //============================================================================================================= +void FiffStream::write_bad_channels(const QStringList& bads) +{ + start_block(FIFFB_MNE_BAD_CHANNELS); + write_name_list(FIFF_MNE_CH_NAME_LIST, bads); + end_block(FIFFB_MNE_BAD_CHANNELS); +} + +//============================================================================================================= + +bool FiffStream::attach_env(const QString& workingDir, const QString& command) +{ + int insert_blocks[] = { FIFFB_MNE , FIFFB_MEAS, FIFFB_MRI, FIFFB_BEM, -1 }; + + int b, k, insert; + FiffTag::SPtr t_pTag; + + FiffId id(FiffId::new_file_id()); + + /* + * Find an appropriate position to insert + */ + for (insert = -1, b = 0; insert_blocks[b] >= 0; b++) { + for (k = 0; k < nent(); k++) { + if (dir()[k]->kind == FIFF_BLOCK_START) { + if (!read_tag(t_pTag, dir()[k]->pos)) + return false; + if (*(t_pTag->toInt()) == insert_blocks[b]) { + insert = k; + break; + } + } + } + if (insert >= 0) + break; + } + if (insert < 0) { + qCritical("Suitable place for environment insertion not found."); + return false; + } + + /* + * Inline fiff_insert_after logic + */ + int where = insert; + if (where < 0 || where >= nent()-1) { + qCritical("illegal insertion point in fiff_insert_after!"); + return false; + } + + FiffTag::SPtr t_pTagNext; + QList old_dir = dir(); + QList this_ent = old_dir.mid(where); + + if (!read_tag(t_pTagNext, this_ent[0]->pos)) + return false; + /* + * Update next info to be sequential + */ + qint64 next_tmp = device()->pos(); + /* + * Go to the end of the file + */ + device()->seek(device()->size()); + /* + * Copy the beginning of old directory + */ + QList new_dir = old_dir.mid(0, where+1); + + qint64 old_end = device()->pos(); + /* + * Write the MNE_ENV block tags + */ + FiffDirEntry::SPtr new_this; + + new_this = FiffDirEntry::SPtr(new FiffDirEntry); + new_this->kind = FIFF_BLOCK_START; + new_this->type = FIFFT_INT; + new_this->size = 1 * 4; + new_this->pos = start_block(FIFFB_MNE_ENV); + new_dir.append(new_this); + + new_this = FiffDirEntry::SPtr(new FiffDirEntry); + new_this->kind = FIFF_BLOCK_ID; + new_this->type = FIFFT_ID_STRUCT; + new_this->size = 5 * 4; + new_this->pos = write_id(FIFF_BLOCK_ID, id); + new_dir.append(new_this); + + new_this = FiffDirEntry::SPtr(new FiffDirEntry); + new_this->kind = FIFF_MNE_ENV_WORKING_DIR; + new_this->type = FIFFT_STRING; + new_this->size = workingDir.size(); + new_this->pos = write_string(FIFF_MNE_ENV_WORKING_DIR, workingDir); + new_dir.append(new_this); + + new_this = FiffDirEntry::SPtr(new FiffDirEntry); + new_this->kind = FIFF_MNE_ENV_COMMAND_LINE; + new_this->type = FIFFT_STRING; + new_this->size = command.size(); + new_this->pos = write_string(FIFF_MNE_ENV_COMMAND_LINE, command); + new_dir.append(new_this); + + new_this = FiffDirEntry::SPtr(new FiffDirEntry); + new_this->kind = FIFF_BLOCK_END; + new_this->type = FIFFT_INT; + new_this->size = 1 * 4; + new_this->pos = end_block(FIFFB_MNE_ENV, next_tmp); + new_dir.append(new_this); + + /* + * Copy the rest of the old directory + */ + new_dir.append(old_dir.mid(where+1)); + /* + * Update the branching tag + */ + t_pTagNext->next = (qint32)old_end; + write_tag(t_pTagNext, this_ent[0]->pos); + + dir() = new_dir; + + return true; +} + +//============================================================================================================= + bool FiffStream::read_cov(const FiffDirNode::SPtr& p_Node, fiff_int_t cov_kind, FiffCov& p_covData) { p_covData.clear(); @@ -1922,11 +2048,14 @@ FiffStream::SPtr FiffStream::start_file(QIODevice& p_IODevice) FiffStream::SPtr p_pStream(new FiffStream(&p_IODevice)); QString t_sFileName = p_pStream->streamName(); - if(!p_pStream->device()->open(QIODevice::WriteOnly)) + if(!p_IODevice.isOpen()) { - qWarning("Cannot write to %s\n", t_sFileName.toUtf8().constData());//consider throw - FiffStream::SPtr p_pEmptyStream; - return p_pEmptyStream; + if(!p_pStream->device()->open(QIODevice::WriteOnly)) + { + qWarning("Cannot write to %s\n", t_sFileName.toUtf8().constData());//consider throw + FiffStream::SPtr p_pEmptyStream; + return p_pEmptyStream; + } } // @@ -2407,15 +2536,15 @@ fiff_long_t FiffStream::write_cov(const FiffCov &p_FiffCov) // fiff_write_float_sparse_rcs(fid,FIFF.FIFF_MNE_COV,cov.data); // else // { - // Store only lower part of covariance matrix + // Store lower triangle including diagonal (matches read_cov format) qint32 dim = p_FiffCov.dim; - qint32 n = ((dim*dim) - dim)/2; + qint32 n = dim*(dim+1)/2; VectorXd vals(n); qint32 count = 0; - for(qint32 i = 1; i < dim; ++i) - for(qint32 j = 0; j < i; ++j) - vals(count) = p_FiffCov.data(i,j); + for(qint32 i = 0; i < dim; ++i) + for(qint32 j = 0; j <= i; ++j) + vals(count++) = p_FiffCov.data(i,j); this->write_double(FIFF_MNE_COV, vals.data(), vals.size()); // } @@ -2609,8 +2738,18 @@ fiff_long_t FiffStream::write_double(fiff_int_t kind, const double* data, fiff_i *this << (qint32)datasize; *this << (qint32)FIFFV_NEXT_SEQ; + // + // Write doubles as raw 8-byte big-endian values. + // FiffStream sets QDataStream::SinglePrecision, which causes + // operator<<(double) to write only 4 bytes. We must bypass that + // to emit the full 8-byte IEEE 754 representation. + // for(qint32 i = 0; i < nel; ++i) - *this << data[i]; + { + qint64 bits; + memcpy(&bits, &data[i], sizeof(double)); + *this << bits; + } return pos; } @@ -3236,6 +3375,16 @@ QList FiffStream::make_dir(bool *ok) if(!this->device()->seek(SEEK_SET)) return dir; while ((pos = this->read_tag_info(t_pTag)) != -1) { + /* + * Guard against reading past EOF. When QDataStream reaches the + * end of the device every subsequent read silently returns zero, + * so read_tag_info never returns -1. Detect this by checking + * whether the stream is still healthy after the header read. + */ + if (this->status() != QDataStream::Ok) { + this->resetStatus(); + break; + } /* * Check that we haven't run into the directory */ @@ -3250,8 +3399,6 @@ QList FiffStream::make_dir(bool *ok) t_pFiffDirEntry->size = t_pTag->size(); t_pFiffDirEntry->pos = (fiff_long_t)pos; - //qDebug() << "Kind: " << t_pTag->kind << "| Type:" << t_pTag->type << "| Size" << t_pTag->size() << "| Next:" << t_pTag->next; - dir.append(t_pFiffDirEntry); if (t_pTag->next < 0) break; diff --git a/src/libraries/fiff/fiff_stream.h b/src/libraries/fiff/fiff_stream.h index 212b5da2afd..6a385ff6f29 100644 --- a/src/libraries/fiff/fiff_stream.h +++ b/src/libraries/fiff/fiff_stream.h @@ -264,6 +264,30 @@ class FIFFSHARED_EXPORT FiffStream : public QDataStream */ QStringList read_bad_channels(const FiffDirNode::SPtr& p_Node); + //========================================================================================================= + /** + * Write a bad channel list as a FIFFB_MNE_BAD_CHANNELS block. + * + * @param[in] bads The list of bad channel names. + */ + void write_bad_channels(const QStringList& bads); + + //========================================================================================================= + /** + * Attach environment metadata (working directory, command line) to this stream. + * Formerly mne_attach_env (SVN MNE). + * + * The stream must already be opened for update (via open_update). + * Finds the first known top-level block (MNE, MEAS, MRI, or BEM) + * and inserts a FIFFB_MNE_ENV block with working directory and + * command-line string. + * + * @param[in] workingDir The working directory to record. + * @param[in] command The command-line string to record. + * @return true on success, false on error. + */ + bool attach_env(const QString& workingDir, const QString& command); + //========================================================================================================= /** * mne_read_cov - also for mne_read_noise_cov diff --git a/src/libraries/fs/CMakeLists.txt b/src/libraries/fs/CMakeLists.txt index 658fe52c459..10305faf340 100644 --- a/src/libraries/fs/CMakeLists.txt +++ b/src/libraries/fs/CMakeLists.txt @@ -11,23 +11,23 @@ find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS ${QT_REQUIRED_COMPONENTS}) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS ${QT_REQUIRED_COMPONENTS}) set(SOURCES - annotation.cpp - colortable.cpp + fs_annotation.cpp + fs_colortable.cpp fs_global.cpp - label.cpp - surface.cpp - annotationset.cpp - surfaceset.cpp + fs_label.cpp + fs_surface.cpp + fs_annotationset.cpp + fs_surfaceset.cpp ) set(HEADERS - annotation.h + fs_annotation.h fs_global.h - colortable.h - label.h - surface.h - annotationset.h - surfaceset.h + fs_colortable.h + fs_label.h + fs_surface.h + fs_annotationset.h + fs_surfaceset.h ) set(FILE_TO_UPDATE fs_global.cpp) diff --git a/src/libraries/fs/annotation.cpp b/src/libraries/fs/fs_annotation.cpp similarity index 99% rename from src/libraries/fs/annotation.cpp rename to src/libraries/fs/fs_annotation.cpp index 7272e19d080..beb9c1486bc 100644 --- a/src/libraries/fs/annotation.cpp +++ b/src/libraries/fs/fs_annotation.cpp @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file annotation.cpp + * @file fs_annotation.cpp * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -38,9 +38,9 @@ // INCLUDES //============================================================================================================= -#include "annotation.h" -#include "label.h" -#include "surface.h" +#include "fs_annotation.h" +#include "fs_label.h" +#include "fs_surface.h" #include diff --git a/src/libraries/fs/annotation.h b/src/libraries/fs/fs_annotation.h similarity index 99% rename from src/libraries/fs/annotation.h rename to src/libraries/fs/fs_annotation.h index af5a04b5707..3ee232b69ed 100644 --- a/src/libraries/fs/annotation.h +++ b/src/libraries/fs/fs_annotation.h @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file annotation.h + * @file fs_annotation.h * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -34,15 +34,15 @@ * */ -#ifndef ANNOTATION_H -#define ANNOTATION_H +#ifndef FS_ANNOTATION_H +#define FS_ANNOTATION_H //============================================================================================================= // INCLUDES //============================================================================================================= #include "fs_global.h" -#include "colortable.h" +#include "fs_colortable.h" //============================================================================================================= // QT INCLUDES @@ -353,4 +353,4 @@ inline QString Annotation::fileName() const } } // NAMESPACE -#endif // ANNOTATION_H +#endif // FS_ANNOTATION_H diff --git a/src/libraries/fs/annotationset.cpp b/src/libraries/fs/fs_annotationset.cpp similarity index 98% rename from src/libraries/fs/annotationset.cpp rename to src/libraries/fs/fs_annotationset.cpp index 74d795babf7..7c7e06aae33 100644 --- a/src/libraries/fs/annotationset.cpp +++ b/src/libraries/fs/fs_annotationset.cpp @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file annotationset.cpp + * @file fs_annotationset.cpp * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -38,8 +38,8 @@ // INCLUDES //============================================================================================================= -#include "annotationset.h" -#include "surfaceset.h" +#include "fs_annotationset.h" +#include "fs_surfaceset.h" #include #include @@ -164,6 +164,9 @@ bool AnnotationSet::read(const QString& p_sLHFileName, const QString& p_sRHFileN } } + if(p_AnnotationSet.m_qMapAnnots.isEmpty()) + return false; + return true; } diff --git a/src/libraries/fs/annotationset.h b/src/libraries/fs/fs_annotationset.h similarity index 98% rename from src/libraries/fs/annotationset.h rename to src/libraries/fs/fs_annotationset.h index 62c9715696e..29829df03ca 100644 --- a/src/libraries/fs/annotationset.h +++ b/src/libraries/fs/fs_annotationset.h @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file annotationset.h + * @file fs_annotationset.h * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -34,15 +34,15 @@ * */ -#ifndef ANNOTATION_SET_H -#define ANNOTATION_SET_H +#ifndef FS_ANNOTATIONSET_H +#define FS_ANNOTATIONSET_H //============================================================================================================= // INCLUDES //============================================================================================================= #include "fs_global.h" -#include "annotation.h" +#include "fs_annotation.h" //============================================================================================================= // QT INCLUDES @@ -270,4 +270,4 @@ inline qint32 AnnotationSet::size() const } } // NAMESPACE -#endif // ANNOTATION_SET_H +#endif // FS_ANNOTATIONSET_H diff --git a/src/libraries/fs/colortable.cpp b/src/libraries/fs/fs_colortable.cpp similarity index 98% rename from src/libraries/fs/colortable.cpp rename to src/libraries/fs/fs_colortable.cpp index 85c9a2fe2fd..bbef2deb403 100644 --- a/src/libraries/fs/colortable.cpp +++ b/src/libraries/fs/fs_colortable.cpp @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file colortable.cpp + * @file fs_colortable.cpp * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -38,7 +38,7 @@ // INCLUDES //============================================================================================================= -#include "colortable.h" +#include "fs_colortable.h" //============================================================================================================= // USED NAMESPACES diff --git a/src/libraries/fs/colortable.h b/src/libraries/fs/fs_colortable.h similarity index 93% rename from src/libraries/fs/colortable.h rename to src/libraries/fs/fs_colortable.h index 88199017f49..ec52d42c879 100644 --- a/src/libraries/fs/colortable.h +++ b/src/libraries/fs/fs_colortable.h @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file colortable.h + * @file fs_colortable.h * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -34,8 +34,14 @@ * */ -#ifndef COLORTABLE_H -#define COLORTABLE_H +#ifndef FS_COLORTABLE_H +#define FS_COLORTABLE_H + +//============================================================================================================= +// FS INCLUDES +//============================================================================================================= + +#include "fs_global.h" //============================================================================================================= // QT INCLUDES @@ -64,7 +70,7 @@ namespace FSLIB * * @brief Vertices label based lookup table */ -class Colortable +class FSSHARED_EXPORT Colortable { public: typedef QSharedPointer SPtr; /**< Shared pointer type for Colortable. */ @@ -145,4 +151,4 @@ inline Eigen::MatrixX4i Colortable::getRGBAs() const } } // NAMESPACE -#endif // COLORTABLE_H +#endif // FS_COLORTABLE_H diff --git a/src/libraries/fs/label.cpp b/src/libraries/fs/fs_label.cpp similarity index 99% rename from src/libraries/fs/label.cpp rename to src/libraries/fs/fs_label.cpp index 8435cfbe05c..0dca043e3c9 100644 --- a/src/libraries/fs/label.cpp +++ b/src/libraries/fs/fs_label.cpp @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file label.cpp + * @file fs_label.cpp * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -38,8 +38,8 @@ // INCLUDES //============================================================================================================= -#include "label.h" -#include "surface.h" +#include "fs_label.h" +#include "fs_surface.h" //============================================================================================================= // QT INCLUDES diff --git a/src/libraries/fs/label.h b/src/libraries/fs/fs_label.h similarity index 98% rename from src/libraries/fs/label.h rename to src/libraries/fs/fs_label.h index 6004c19b410..c9110793bff 100644 --- a/src/libraries/fs/label.h +++ b/src/libraries/fs/fs_label.h @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file label.h + * @file fs_label.h * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -34,8 +34,8 @@ * */ -#ifndef LABEL_H -#define LABEL_H +#ifndef FS_LABEL_H +#define FS_LABEL_H //============================================================================================================= // INCLUDES @@ -192,4 +192,4 @@ inline bool Label::isEmpty() const Q_DECLARE_METATYPE(FSLIB::Label); #endif -#endif // LABEL_H +#endif // FS_LABEL_H diff --git a/src/libraries/fs/surface.cpp b/src/libraries/fs/fs_surface.cpp similarity index 99% rename from src/libraries/fs/surface.cpp rename to src/libraries/fs/fs_surface.cpp index 9e298336c32..b24d34e27e3 100644 --- a/src/libraries/fs/surface.cpp +++ b/src/libraries/fs/fs_surface.cpp @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file surface.cpp + * @file fs_surface.cpp * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -38,7 +38,7 @@ // INCLUDES //============================================================================================================= -#include "surface.h" +#include "fs_surface.h" #include #include diff --git a/src/libraries/fs/surface.h b/src/libraries/fs/fs_surface.h similarity index 99% rename from src/libraries/fs/surface.h rename to src/libraries/fs/fs_surface.h index 84b532b7742..3d4b85ad05e 100644 --- a/src/libraries/fs/surface.h +++ b/src/libraries/fs/fs_surface.h @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file surface.h + * @file fs_surface.h * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -34,8 +34,8 @@ * */ -#ifndef SURFACE_H -#define SURFACE_H +#ifndef FS_SURFACE_H +#define FS_SURFACE_H //============================================================================================================= // INCLUDES @@ -374,4 +374,4 @@ inline QString Surface::fileName() const } } // NAMESPACE -#endif // SURFACE_H +#endif // FS_SURFACE_H diff --git a/src/libraries/fs/surfaceset.cpp b/src/libraries/fs/fs_surfaceset.cpp similarity index 98% rename from src/libraries/fs/surfaceset.cpp rename to src/libraries/fs/fs_surfaceset.cpp index 7408268e3c1..6bb3c4dc59b 100644 --- a/src/libraries/fs/surfaceset.cpp +++ b/src/libraries/fs/fs_surfaceset.cpp @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file surfaceset.cpp + * @file fs_surfaceset.cpp * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -38,7 +38,7 @@ // INCLUDES //============================================================================================================= -#include "surfaceset.h" +#include "fs_surfaceset.h" //============================================================================================================= // QT INCLUDES @@ -178,6 +178,9 @@ bool SurfaceSet::read(const QString& p_sLHFileName, const QString& p_sRHFileName } } + if(p_SurfaceSet.m_qMapSurfs.isEmpty()) + return false; + p_SurfaceSet.calcOffset(); return true; diff --git a/src/libraries/fs/surfaceset.h b/src/libraries/fs/fs_surfaceset.h similarity index 98% rename from src/libraries/fs/surfaceset.h rename to src/libraries/fs/fs_surfaceset.h index aaa092d7d5e..1db0a67f9c3 100644 --- a/src/libraries/fs/surfaceset.h +++ b/src/libraries/fs/fs_surfaceset.h @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file surfaceset.h + * @file fs_surfaceset.h * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh @@ -34,15 +34,15 @@ * */ -#ifndef SURFACESET_H -#define SURFACESET_H +#ifndef FS_SURFACESET_H +#define FS_SURFACESET_H //============================================================================================================= // INCLUDES //============================================================================================================= #include "fs_global.h" -#include "surface.h" +#include "fs_surface.h" //============================================================================================================= // QT INCLUDES @@ -267,5 +267,5 @@ inline qint32 SurfaceSet::size() const } } // NAMESPACE -#endif // SURFACESET_H +#endif // FS_SURFACESET_H diff --git a/src/libraries/fwd/CMakeLists.txt b/src/libraries/fwd/CMakeLists.txt index 56ae1da6f45..2b5990440a3 100644 --- a/src/libraries/fwd/CMakeLists.txt +++ b/src/libraries/fwd/CMakeLists.txt @@ -11,8 +11,9 @@ find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS ${QT_REQUIRED_COMPONENTS}) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS ${QT_REQUIRED_COMPONENTS}) set(SOURCES - computeFwd/compute_fwd_settings.cpp - computeFwd/compute_fwd.cpp + compute_fwd/compute_fwd.cpp + compute_fwd/compute_fwd_settings.cpp + fwd_forward_solution.cpp fwd_bem_model.cpp fwd_bem_solution.cpp fwd_coil.cpp @@ -28,8 +29,10 @@ set(SOURCES set(HEADERS fwd_global.h - computeFwd/compute_fwd_settings.h - computeFwd/compute_fwd.h + fwd.h + compute_fwd/compute_fwd.h + compute_fwd/compute_fwd_settings.h + fwd_forward_solution.h fwd_bem_model.h fwd_bem_solution.h fwd_coil.h diff --git a/src/libraries/fwd/computeFwd/compute_fwd.cpp b/src/libraries/fwd/computeFwd/compute_fwd.cpp deleted file mode 100644 index 0d5ebc7f2c4..00000000000 --- a/src/libraries/fwd/computeFwd/compute_fwd.cpp +++ /dev/null @@ -1,2361 +0,0 @@ - - -#include "compute_fwd.h" - -#include -#include -#include "../fwd_coil_set.h" -#include "../fwd_comp_data.h" -#include -#include "../fwd_eeg_sphere_model_set.h" -#include "../fwd_bem_model.h" - -#include -#include -#include -#include - -#include - -#include - -#include - -#include - -#include - -#include -#include -#include -#include - -using namespace Eigen; -using namespace FWDLIB; -using namespace FIFFLIB; -using namespace MNELIB; - -#ifndef TRUE -#define TRUE 1 -#endif - -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef FAIL -#define FAIL -1 -#endif - -#ifndef OK -#define OK 0 -#endif - -#define X_41 0 -#define Y_41 1 -#define Z_41 2 - -#define ALLOC_ICMATRIX_41(x,y) mne_imatrix_41((x),(y)) - -#define MALLOC_41(x,t) (t *)malloc((x)*sizeof(t)) -#define REALLOC_41(x,y,t) (t *)((x == Q_NULLPTR) ? malloc((y)*sizeof(t)) : realloc((x),(y)*sizeof(t))) - -#define FREE_41(x) if ((char *)(x) != Q_NULLPTR) free((char *)(x)) - -#define VEC_COPY_41(to,from) {\ - (to)[X_41] = (from)[X_41];\ - (to)[Y_41] = (from)[Y_41];\ - (to)[Z_41] = (from)[Z_41];\ - } - -#define VEC_DOT_41(x,y) ((x)[X_41]*(y)[X_41] + (x)[Y_41]*(y)[Y_41] + (x)[Z_41]*(y)[Z_41]) - -#define VEC_LEN_41(x) sqrt(VEC_DOT_41(x,x)) - -#define ALLOC_CMATRIX_41(x,y) mne_cmatrix_41((x),(y)) -#define FREE_CMATRIX_41(m) mne_free_cmatrix_41((m)) -#define FREE_ICMATRIX_41(m) mne_free_icmatrix_41((m)) - -static void matrix_error_41(int kind, int nr, int nc) - -{ - if (kind == 1) - printf("Failed to allocate memory pointers for a %d x %d matrix\n",nr,nc); - else if (kind == 2) - printf("Failed to allocate memory for a %d x %d matrix\n",nr,nc); - else - printf("Allocation error for a %d x %d matrix\n",nr,nc); - if (sizeof(void *) == 4) { - printf("This is probably because you seem to be using a computer with 32-bit architecture.\n"); - printf("Please consider moving to a 64-bit platform."); - } - printf("Cannot continue. Sorry.\n"); - exit(1); -} - -float **mne_cmatrix_41(int nr,int nc) - -{ - int i; - float **m; - float *whole; - - m = MALLOC_41(nr,float *); - if (!m) matrix_error_41(1,nr,nc); - whole = MALLOC_41(nr*nc,float); - if (!whole) matrix_error_41(2,nr,nc); - - for(i=0;iopen()) { - stream->close(); - return Q_NULLPTR; - } - else { - id = MALLOC_41(1,fiffIdRec); - id->version = stream->id().version; /**< File version. */ - id->machid[0] = stream->id().machid[0]; /**< Unique machine ID. */ - id->machid[1] = stream->id().machid[1]; - id->time = stream->id().time; /**< Time of the ID creation. */ - - stream->close(); - return id; - } -} - -//============================= mne_read_forward_solution.c ============================= - -//int mne_read_meg_comp_eeg_ch_info_41(const QString& name, -// QList& megp, /* MEG channels */ -// int* nmegp, -// QList& meg_compp, -// int* nmeg_compp, -// QList& eegp, /* EEG channels */ -// int* neegp, -// FiffCoordTransOld** meg_head_t, -// fiffId* idp) /* The measurement ID */ -///* -// * Read the channel information and split it into three arrays, -// * one for MEG, one for MEG compensation channels, and one for EEG -// */ -//{ -// QFile file(name); -// FiffStream::SPtr stream(new FiffStream(&file)); - -// QList chs; -// int nchan = 0; -// QList meg; -// int nmeg = 0; -// QList meg_comp; -// int nmeg_comp = 0; -// QList eeg; -// int neeg = 0; -// fiffId id = Q_NULLPTR; -// QList nodes; -// FiffDirNode::SPtr info; -// FiffTag::SPtr t_pTag; -// FiffChInfo this_ch; -// FiffCoordTransOld* t = Q_NULLPTR; -// fiff_int_t kind, pos; -// int j,k,to_find; - -// if(!stream->open()) -// goto bad; - -// nodes = stream->dirtree()->dir_tree_find(FIFFB_MNE_PARENT_MEAS_FILE); - -// if (nodes.size() == 0) { -// nodes = stream->dirtree()->dir_tree_find(FIFFB_MEAS_INFO); -// if (nodes.size() == 0) { -// qCritical ("Could not find the channel information."); -// goto bad; -// } -// } -// info = nodes[0]; -// to_find = 0; -// for (k = 0; k < info->nent(); k++) { -// kind = info->dir[k]->kind; -// pos = info->dir[k]->pos; -// switch (kind) { -// case FIFF_NCHAN : -// if (!stream->read_tag(t_pTag,pos)) -// goto bad; -// nchan = *t_pTag->toInt(); - -// for (j = 0; j < nchan; j++) { -// chs.append(FiffChInfo()); -// chs[j].scanNo = -1; -// } -// to_find = nchan; -// break; - -// case FIFF_PARENT_BLOCK_ID : -// if(!stream->read_tag(t_pTag, pos)) -// goto bad; -// // id = t_pTag->toFiffID(); -// *id = *(fiffId)t_pTag->data(); -// break; - -// case FIFF_COORD_TRANS : -// if(!stream->read_tag(t_pTag, pos)) -// goto bad; -// // t = t_pTag->toCoordTrans(); -// t = FiffCoordTransOld::read_helper( t_pTag ); -// if (t->from != FIFFV_COORD_DEVICE || t->to != FIFFV_COORD_HEAD) -// t = Q_NULLPTR; -// break; - -// case FIFF_CH_INFO : /* Information about one channel */ -// if(!stream->read_tag(t_pTag, pos)) -// goto bad; -// this_ch = t_pTag->toChInfo(); -// if (this_ch.scanNo <= 0 || this_ch.scanNo > nchan) { -// printf ("FIFF_CH_INFO : scan # out of range %d (%d)!",this_ch.scanNo,nchan); -// goto bad; -// } -// else -// chs[this_ch.scanNo-1] = this_ch; -// to_find--; -// break; -// } -// } -// if (to_find != 0) { -// qCritical("Some of the channel information was missing."); -// goto bad; -// } -// if (t == Q_NULLPTR && meg_head_t != Q_NULLPTR) { -// /* -// * Try again in a more general fashion -// */ -// if ((t = FiffCoordTransOld::mne_read_meas_transform(name)) == Q_NULLPTR) { -// qCritical("MEG -> head coordinate transformation not found."); -// goto bad; -// } -// } -// /* -// * Sort out the channels -// */ -// for (k = 0; k < nchan; k++) { -// if (chs[k].kind == FIFFV_MEG_CH) { -// meg.append(chs[k]); -// nmeg++; -// } else if (chs[k].kind == FIFFV_REF_MEG_CH) { -// meg_comp.append(chs[k]); -// nmeg_comp++; -// } else if (chs[k].kind == FIFFV_EEG_CH) { -// eeg.append(chs[k]); -// neeg++; -// } -// } -// // fiff_close(in); - -// stream->close(); - -// megp = meg; -// if(nmegp) { -// *nmegp = nmeg; -// } - -// meg_compp = meg_comp; -// if(nmeg_compp) { -// *nmeg_compp = nmeg_comp; -// } - -// eegp = eeg; -// if(neegp) { -// *neegp = neeg; -// } - -// if (idp == Q_NULLPTR) { -// FREE_41(id); -// } else { -// *idp = id; -// } - -// if (meg_head_t == Q_NULLPTR) { -// FREE_41(t); -// } else { -// *meg_head_t = t; -// } - -// return FIFF_OK; - -//bad : { -// // fiff_close(in); -// stream->close(); -// FREE_41(id); -// // FREE_41(tag.data); -// FREE_41(t); -// return FIFF_FAIL; -// } -//} - -int ComputeFwd::mne_read_meg_comp_eeg_ch_info_41(FIFFLIB::FiffInfoBase::SPtr pFiffInfoBase, - QList& listMegCh, - int& iNMeg, - QList& listMegComp, - int& iNMegCmp, - QList& listEegCh, - int &iNEeg, - FiffCoordTrans& transDevHead, - FiffId& id) -{ - int iNumCh = pFiffInfoBase->nchan; - for (int k = 0; k < iNumCh; k++) { - if (pFiffInfoBase->chs[k].kind == FIFFV_MEG_CH) { - listMegCh.append(pFiffInfoBase->chs[k]); - iNMeg++; - } else if (pFiffInfoBase->chs[k].kind == FIFFV_REF_MEG_CH) { - listMegComp.append(pFiffInfoBase->chs[k]); - iNMegCmp++; - } else if (pFiffInfoBase->chs[k].kind == FIFFV_EEG_CH) { - listEegCh.append(pFiffInfoBase->chs[k]); - iNEeg++; - } - } - - if(m_pSettings->meg_head_t.isEmpty()) { - transDevHead = pFiffInfoBase->dev_head_t; - } else { - transDevHead = m_pSettings->meg_head_t; - } - if(m_meg_head_t.isEmpty()) { - qCritical("MEG -> head coordinate transformation not found."); - return FIFF_FAIL; - } - id = pFiffInfoBase->meas_id; - return OK; -} - -int mne_check_chinfo(const QList& chs, - int nch) -/* - * Check that all EEG channels have reasonable locations - */ -{ - int k; - FiffChInfo ch; - float close = 0.02f; - - for (k = 0; k < nch; k++) { - if (chs.at(k).kind == FIFFV_EEG_CH) { - if (chs.at(k).chpos.r0.norm() < close) { - qCritical("Some EEG channels do not have locations assigned."); - return FAIL; - } - } - } - - return OK; -} - -//============================================================================================================= -// Temporary Helpers -//============================================================================================================= - -void write_id_old(FiffStream::SPtr& t_pStream, fiff_int_t kind, fiffId id) -{ - fiffId t_id = id; - if(t_id->version == -1) - { - /* initialize random seed: */ - srand ( time(Q_NULLPTR) ); - double rand_1 = (double)(rand() % 100);rand_1 /= 100; - double rand_2 = (double)(rand() % 100);rand_2 /= 100; - - time_t seconds; - seconds = time (Q_NULLPTR); - - //fiff_int_t timezone = 5; // Matlab does not know the timezone - t_id->version = (1 << 16) | 2; // Version (1 << 16) | 2 - t_id->machid[0] = 65536*rand_1; // Machine id is random for now - t_id->machid[1] = 65536*rand_2; // Machine id is random for now - t_id->time.secs = (int)seconds; //seconds since January 1, 1970 //3600*(24*(now-datenum(1970,1,1,0,0,0))+timezone); - t_id->time.usecs = 0; // Do not know how we could get this - } - - // - // - fiff_int_t datasize = 5*4; // The id comprises five integers - - *t_pStream << (qint32)kind; - *t_pStream << (qint32)FIFFT_ID_STRUCT; - *t_pStream << (qint32)datasize; - *t_pStream << (qint32)FIFFV_NEXT_SEQ; - // - // Collect the bits together for one write - // - qint32 data[5]; - data[0] = t_id->version; - data[1] = t_id->machid[0]; - data[2] = t_id->machid[1]; - data[3] = t_id->time.secs; - data[4] = t_id->time.usecs; - - for(qint32 i = 0; i < 5; ++i) - *t_pStream << data[i]; -} - -//============================================================================================================= - -void write_coord_trans_old(FiffStream::SPtr& t_pStream, const FiffCoordTrans& trans) -{ - //?typedef struct _fiffCoordTransRec { - // fiff_int_t from; /*!< Source coordinate system. */ - // fiff_int_t to; /*!< Destination coordinate system. */ - // fiff_float_t rot[3][3]; /*!< The forward transform (rotation part) */ - // fiff_float_t move[3]; /*!< The forward transform (translation part) */ - // fiff_float_t invrot[3][3]; /*!< The inverse transform (rotation part) */ - // fiff_float_t invmove[3]; /*!< The inverse transform (translation part) */ - //} *fiffCoordTrans, fiffCoordTransRec; /*!< Coordinate transformation descriptor */ - fiff_int_t datasize = 4*2*12 + 4*2; - - *t_pStream << (qint32)FIFF_COORD_TRANS; - *t_pStream << (qint32)FIFFT_COORD_TRANS_STRUCT; - *t_pStream << (qint32)datasize; - *t_pStream << (qint32)FIFFV_NEXT_SEQ; - - // - // Start writing fiffCoordTransRec - // - *t_pStream << (qint32)trans.from; - *t_pStream << (qint32)trans.to; - - // - // The transform... - // - qint32 r, c; - for (r = 0; r < 3; ++r) - for (c = 0; c < 3; ++c) - *t_pStream << (float)trans.rot()(r,c); - for (r = 0; r < 3; ++r) - *t_pStream << (float)trans.move()(r); - - // - // ...and its inverse - // - for (r = 0; r < 3; ++r) - for (c = 0; c < 3; ++c) - *t_pStream << (float)trans.invrot()(r,c); - for (r = 0; r < 3; ++r) - *t_pStream << (float)trans.invmove()(r); -} - -// Write a coordinate transform to a FIFF stream. -void write_coord_trans(FiffStream::SPtr& t_pStream, const FiffCoordTrans& trans) -{ - if (trans.isEmpty()) return; - write_coord_trans_old(t_pStream, trans); -} - -static int **make_file_triangle_list_41(int **tris, int ntri) -/* - * In the file the numbering starts from one - */ -{ - int **res = ALLOC_ICMATRIX_41(ntri,3); - int j,k; - - for (j = 0; j < ntri; j++) - for (k = 0; k < 3; k++) - res[j][k] = tris[j][k]+1; - return res; -} - -void mne_write_bad_channel_list_new(FiffStream::SPtr& t_pStream, const QStringList& t_badList)//FILE *out, char **list, int nlist) -{ - - t_pStream->start_block(FIFFB_MNE_BAD_CHANNELS); - t_pStream->write_name_list(FIFF_MNE_CH_NAME_LIST,t_badList); - t_pStream->end_block(FIFFB_MNE_BAD_CHANNELS); - - ///////////////////////////////////////////////////////// - - // fiff_int_t bad_channel_block = FIFFB_MNE_BAD_CHANNELS; - // fiffTagRec bad_channel_block_tags[] = { - // { FIFF_BLOCK_START, FIFFT_INT, 0, FIFFV_NEXT_SEQ, Q_NULLPTR }, - // { FIFF_MNE_CH_NAME_LIST, FIFFT_STRING, 0, FIFFV_NEXT_SEQ, Q_NULLPTR }, - // { FIFF_BLOCK_END, FIFFT_INT, 0, FIFFV_NEXT_SEQ, Q_NULLPTR }}; - // int nbad_channel_block_tags = 3; - // char *names = Q_NULLPTR; - // int k; - - // if (nlist <= 0) - // return OK; - - // names = mne_name_list_to_string(list,nlist); - // bad_channel_block_tags[0].size = sizeof(fiff_int_t); - // bad_channel_block_tags[0].data = &bad_channel_block; - - // bad_channel_block_tags[1].size = strlen(names); - // bad_channel_block_tags[1].data = names; - - // bad_channel_block_tags[2].size = sizeof(fiff_int_t); - // bad_channel_block_tags[2].data = &bad_channel_block; - - // for (k = 0; k < nbad_channel_block_tags; k++) - // if (fiff_write_tag(out,bad_channel_block_tags+k) == FIFF_FAIL) { - // FREE(names); - // return FAIL; - // } - // FREE(names); - // return OK; -} - -void fiff_write_float_matrix_old ( FiffStream::SPtr& t_pStream, /* Destination file name */ - int kind, /* What kind of tag */ - fiff_float_t **data, /* The data to write */ - int rows, - int cols) /* Number of rows and columns */ -/* - * Write out a 2D floating-point matrix - */ -{ - MatrixXf mat(rows,cols); - - for (int i = 0; i < rows; ++i) { - for(int j = 0; j < cols; ++j) { - mat(i,j) = data[i][j]; - } - } - - t_pStream->write_float_matrix(kind, mat); - - // int res,*dims; - // fiffTagRec tag; - //#ifdef INTEL_X86_ARCH - // int c; - //#endif - // int k; - // int rowsize; - - // tag.kind = kind; - // tag.type = FIFFT_FLOAT | FIFFT_MATRIX; - // tag.size = rows*cols*sizeof(fiff_float_t) + 3*sizeof(fiff_int_t); - // tag.data = Q_NULLPTR; - // tag.next = FIFFV_NEXT_SEQ; - - // if ((res = fiff_write_tag_info(out,&tag)) == -1) - // return FIFF_FAIL; - - // rowsize = cols*sizeof(fiff_float_t); - // for (k = 0; k < rows; k++) { - //#ifdef INTEL_X86_ARCH - // for (c = 0; c < cols; c++) - // swap_float(data[k]+c); - //#endif - // if (fwrite (data[k],rowsize,1,out) != 1) { - // if (ferror(out)) - // err_set_sys_error("fwrite"); - // else - // err_set_error("fwrite failed"); - //#ifdef INTEL_X86_ARCH - // for (c = 0; c < cols; c++) - // swap_float(data[k]+c); - //#endif - // return FIFF_FAIL; - // } - //#ifdef INTEL_X86_ARCH - // for (c = 0; c < cols; c++) - // swap_float(data[k]+c); - //#endif - // } - // dims = MALLOC_41(3,fiff_int_t); - // dims[0] = swap_int(cols); - // dims[1] = swap_int(rows); - // dims[2] = swap_int(2); - // if (fwrite (dims,3*sizeof(fiff_int_t),1,out) != 1) { - // if (ferror(out)) - // err_set_sys_error("fwrite"); - // else - // err_set_error("fwrite failed"); - // FREE(dims); - // return FIFF_FAIL; - // } - // FREE(dims); - // return res; -} - -void fiff_write_int_matrix_old ( FiffStream::SPtr& t_pStream, - int kind, /* What kind of tag */ - fiff_int_t **data, /* The data to write */ - int rows, - int cols) /* Number of rows and columns */ -/* - * Write out a 2D integer matrix - */ -{ - MatrixXi mat(rows,cols); - - for (int i = 0; i < rows; ++i) { - for(int j = 0; j < cols; ++j) { - mat(i,j) = data[i][j]; - } - } - - t_pStream->write_int_matrix(kind, mat); - - // int res,*dims; - // fiffTagRec tag; - //#ifdef INTEL_X86_ARCH - // int c; - //#endif - // int k; - // int rowsize; - - // tag.kind = kind; - // tag.type = FIFFT_INT | FIFFT_MATRIX; - // tag.size = rows*cols*sizeof(fiff_int_t) + 3*sizeof(fiff_int_t); - // tag.data = Q_NULLPTR; - // tag.next = FIFFV_NEXT_SEQ; - - // if ((res = fiff_write_tag_info(out,&tag)) == -1) - // return -1; - - // rowsize = cols*sizeof(fiff_int_t); - // for (k = 0; k < rows; k++) { - //#ifdef INTEL_X86_ARCH - // for (c = 0; c < cols; c++) - // data[k][c] = swap_int(data[k][c]); - //#endif - // if (fwrite (data[k],rowsize,1,out) != 1) { - // if (ferror(out)) - // err_set_sys_error("fwrite"); - // else - // err_set_error("fwrite failed"); - // return -1; - //#ifdef INTEL_X86_ARCH - // for (c = 0; c < cols; c++) - // data[k][c] = swap_int(data[k][c]); - //#endif - // } - //#ifdef INTEL_X86_ARCH - // for (c = 0; c < cols; c++) - // data[k][c] = swap_int(data[k][c]); - //#endif - // } - // dims = MALLOC_41(3,fiff_int_t); - // dims[0] = swap_int(cols); - // dims[1] = swap_int(rows); - // dims[2] = swap_int(2); - // if (fwrite (dims,3*sizeof(fiff_int_t),1,out) != 1) { - // if (ferror(out)) - // err_set_sys_error("fwrite"); - // else - // err_set_error("fwrite failed"); - // FREE(dims); - // return -1; - // } - // FREE(dims); - // return res; -} - -int fiff_write_float_sparse_matrix_old(FiffStream::SPtr& t_pStream, int kind, FiffSparseMatrix* mat) -/* - * Write a sparse matrix - */ -{ - FiffTag::SPtr tag; - int k; - int type; - int datasize,idxsize,ptrsize; - int two = 2; - - datasize = mat->nz * sizeof(fiff_float_t); - idxsize = mat->nz * sizeof(fiff_int_t); - if (mat->coding == FIFFTS_MC_CCS) - ptrsize = (mat->n+1) * sizeof(fiff_int_t); - else if (mat->coding == FIFFTS_MC_RCS) - ptrsize = (mat->m+1) * sizeof(fiff_int_t); - else { - qCritical("Incomprehensible sparse matrix coding"); - return FIFF_FAIL; - } - - if (datasize <= 0 || idxsize <= 0 || ptrsize <= 0) { - qCritical("fiff_write_float_ccs_matrix: negative vector size(s) in sparse matrix!\n"); - return FIFF_FAIL; - } - -// tag.kind = kind; - if (mat->coding == FIFFTS_MC_CCS) - type = FIFFT_FLOAT | FIFFT_CCS_MATRIX;//tag.type = FIFFT_FLOAT | FIFFT_CCS_MATRIX; - else if (mat->coding == FIFFTS_MC_RCS) - type = FIFFT_FLOAT | FIFFT_RCS_MATRIX;//tag.type = FIFFT_FLOAT | FIFFT_RCS_MATRIX; - else { - qCritical("Incomprehensible sparse matrix coding"); - return FIFF_FAIL; - } - -// tag.size = datasize+idxsize+ptrsize+4*sizeof(fiff_int_t); -// tag.data = Q_NULLPTR; -// tag.next = FIFFV_NEXT_SEQ; - - //Write Tag Info - *t_pStream << (qint32)kind; - *t_pStream << (qint32)type; - *t_pStream << (qint32)(datasize+idxsize+ptrsize+4*sizeof(fiff_int_t)); - *t_pStream << (qint32)FIFFV_NEXT_SEQ; -// if (fiff_write_tag_info(out,&tag) == FIFF_FAIL) -// return FIFF_FAIL; - - /* - * Write data - */ - for(k = 0; k < mat->nz; ++k) - *t_pStream << mat->data[k]; - -// /* -// * Write data with swapping -// */ -//#ifdef INTEL_X86_ARCH -// for (k = 0; k < mat->nz; k++) -// swap_floatp(mat->data+k); -//#endif -// res = fwrite (mat->data,datasize,1,out); -//#ifdef INTEL_X86_ARCH -// for (k = 0; k < mat->nz; k++) -// swap_floatp(mat->data+k); -//#endif -// if (res != 1) -// goto fwrite_error; - - /* - * Write indexes - */ - for(k = 0; k < mat->nz; ++k) - *t_pStream << mat->inds[k]; - -// /* -// * Write indexes with swapping -// */ -//#ifdef INTEL_X86_ARCH -// for (k = 0; k < mat->nz; k++) -// swap_intp(mat->inds+k); -//#endif -// res = fwrite (mat->inds,idxsize,1,out); -//#ifdef INTEL_X86_ARCH -// for (k = 0; k < mat->nz; k++) -// swap_intp(mat->inds+k); -//#endif -// if (res != 1) -// goto fwrite_error; - - if (mat->coding == FIFFTS_MC_CCS) { - - for(k = 0; k < mat->n+1; ++k) - *t_pStream << mat->ptrs[k]; - -//#ifdef INTEL_X86_ARCH -// for (k = 0; k < mat->n+1; k++) -// swap_intp(mat->ptrs+k); -//#endif -// res = fwrite (mat->ptrs,ptrsize,1,out); -//#ifdef INTEL_X86_ARCH -// for (k = 0; k < mat->n+1; k++) -// swap_intp(mat->ptrs+k); -//#endif -// if (res != 1) -// goto fwrite_error; - } - else { /* Row-compressed format */ - - for(k = 0; k < mat->m+1; ++k) - *t_pStream << mat->ptrs[k]; - -//#ifdef INTEL_X86_ARCH -// for (k = 0; k < mat->m+1; k++) -// swap_intp(mat->ptrs+k); -//#endif -// res = fwrite (mat->ptrs,ptrsize,1,out); -//#ifdef INTEL_X86_ARCH -// for (k = 0; k < mat->m+1; k++) -// swap_intp(mat->ptrs+k); -//#endif -// if (res != 1) -// goto fwrite_error; - } - /* - * Write the dimensions - */ - *t_pStream << (qint32)mat->nz; -// val = swap_int(mat->nz); -// if (fwrite (&val,sizeof(fiff_int_t),1,out) != 1) -// goto fwrite_error; - - *t_pStream << (qint32)mat->m; -// val = swap_int(mat->m); -// if (fwrite (&val,sizeof(fiff_int_t),1,out) != 1) -// goto fwrite_error; - - *t_pStream << (qint32)mat->n; -// val = swap_int(mat->n); -// if (fwrite (&val,sizeof(fiff_int_t),1,out) != 1) -// goto fwrite_error; - - *t_pStream << (qint32)two; -// val = swap_int(two); -// if (fwrite (&val,sizeof(fiff_int_t),1,out) != 1) -// goto fwrite_error; - return FIFF_OK; - -//fwrite_error : { -//// if (ferror(out)) -//// qCritical("fwrite"); -//// else -//// err_set_error("fwrite failed"); -// return FIFF_FAIL; -// } -} - -FiffSparseMatrix* mne_create_sparse_rcs(int nrow, /* Number of rows */ - int ncol, /* Number of columns */ - int *nnz, /* Number of non-zero elements on each row */ - int **colindex, /* Column indices of non-zero elements on each row */ - float **vals) /* The nonzero elements on each row - * If Q_NULLPTR, the matrix will be all zeroes */ - -{ - int j,k,nz,ptr,ind; - - for (j = 0, nz = 0; j < nrow; j++) - nz = nz + nnz[j]; - - if (nz <= 0) { - qCritical("No nonzero elements specified."); - return Q_NULLPTR; - } - - FiffSparseMatrix* sparse = new FiffSparseMatrix; - sparse->coding = FIFFTS_MC_RCS; - sparse->m = nrow; - sparse->n = ncol; - sparse->nz = nz; - sparse->data.resize(nz); - sparse->data.setZero(); - sparse->inds.resize(nz); - sparse->inds.setZero(); - sparse->ptrs.resize(nrow + 1); - sparse->ptrs.setZero(); - - for (j = 0, nz = 0; j < nrow; j++) { - ptr = -1; - for (k = 0; k < nnz[j]; k++) { - if (ptr < 0) - ptr = nz; - ind = sparse->inds[nz] = colindex[j][k]; - if (ind < 0 || ind >= ncol) { - qCritical("Column index out of range in mne_create_sparse_rcs"); - delete sparse; - return Q_NULLPTR; - } - if (vals) - sparse->data[nz] = vals[j][k]; - else - sparse->data[nz] = 0.0; - nz++; - } - sparse->ptrs[j] = ptr; - } - sparse->ptrs[nrow] = nz; - for (j = nrow-1; j >= 0; j--) /* Take care of the empty rows */ - if (sparse->ptrs[j] < 0) - sparse->ptrs[j] = sparse->ptrs[j+1]; - return sparse; -} - -FiffSparseMatrix* mne_pick_lower_triangle_rcs(FiffSparseMatrix* mat) -/* - * Fill in upper triangle with the lower triangle values - */ -{ - int i,j,k; - - if (mat->coding != FIFFTS_MC_RCS) { - qCritical("The input matrix to mne_add_upper_triangle_rcs must be in RCS format"); - return Q_NULLPTR; - } - if (mat->m != mat->n) { - qCritical("The input matrix to mne_pick_lower_triangle_rcs must be square"); - return Q_NULLPTR; - } - /* - * Pick the lower triangle elements - */ - std::vector nnz(mat->m); - std::vector> colindex(mat->m); - std::vector> vals(mat->m); - - for (i = 0; i < mat->m; i++) { - nnz[i] = mat->ptrs[i+1] - mat->ptrs[i]; - if (nnz[i] > 0) { - colindex[i].resize(nnz[i]); - vals[i].resize(nnz[i]); - for (j = mat->ptrs[i], k = 0; j < mat->ptrs[i+1]; j++) { - if (mat->inds[j] <= i) { - vals[i][k] = mat->data[j]; - colindex[i][k] = mat->inds[j]; - k++; - } - } - nnz[i] = k; - } - } - /* - * Assemble the matrix — build raw pointer arrays for the C-style interface - */ - std::vector ci_ptrs(mat->m); - std::vector val_ptrs(mat->m); - for (i = 0; i < mat->m; i++) { - ci_ptrs[i] = colindex[i].empty() ? nullptr : colindex[i].data(); - val_ptrs[i] = vals[i].empty() ? nullptr : vals[i].data(); - } - - return mne_create_sparse_rcs(mat->m, mat->n, nnz.data(), ci_ptrs.data(), val_ptrs.data()); -} - -static int write_volume_space_info(FiffStream::SPtr& t_pStream, MNESourceSpace* ss, int selected_only) -/* - * Write the vertex neighbors and other information for a volume source space - */ -{ - int ntot,nvert; - int *nneighbors = Q_NULLPTR; - int *neighbors = Q_NULLPTR; - int *inuse_map = Q_NULLPTR; - int nneigh; - int k,p; - int res = FAIL; - - if (ss->type != FIFFV_MNE_SPACE_VOLUME) - return OK; - if (ss->neighbor_vert.empty() || ss->nneighbor_vert.size() == 0) - return OK; - if (selected_only) { - inuse_map = MALLOC_41(ss->np,int); - for (k = 0,p = 0, ntot = 0; k < ss->np; k++) { - if (ss->inuse[k]) { - ntot += ss->nneighbor_vert[k]; - inuse_map[k] = p++; - } - else - inuse_map[k] = -1; - } - nneighbors = MALLOC_41(ss->nuse,int); - neighbors = MALLOC_41(ntot,int); - /* - * Pick the neighbors and fix the vertex numbering to refer - * to the vertices in use only - */ - for (k = 0, nvert = 0, ntot = 0; k < ss->np; k++) { - if (ss->inuse[k]) { - const Eigen::VectorXi& neigh = ss->neighbor_vert[k]; - nneigh = ss->nneighbor_vert[k]; - nneighbors[nvert++] = nneigh; - for (p = 0; p < nneigh; p++) - neighbors[ntot++] = neigh[p] < 0 ? -1 : inuse_map[neigh[p]]; - } - } - } - else { - for (k = 0, ntot = 0; k < ss->np; k++) - ntot += ss->nneighbor_vert[k]; - nneighbors = MALLOC_41(ss->np,int); - neighbors = MALLOC_41(ntot,int); - nvert = ss->np; - for (k = 0, ntot = 0; k < ss->np; k++) { - const Eigen::VectorXi& neigh = ss->neighbor_vert[k]; - nneigh = ss->nneighbor_vert[k]; - nneighbors[k] = nneigh; - for (p = 0; p < nneigh; p++) - neighbors[ntot++] = neigh[p]; - } - } - - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NNEIGHBORS,nneighbors,nvert); - // tag.next = FIFFV_NEXT_SEQ; - // tag.kind = FIFF_MNE_SOURCE_SPACE_NNEIGHBORS; - // tag.type = FIFFT_INT; - // tag.size = nvert*sizeof(fiff_int_t); - // tag.data = (fiff_byte_t *)nneighbors; - // if (fiff_write_tag(out,&tag) == FIFF_FAIL) - // goto out; - - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NEIGHBORS,neighbors,ntot); - // tag.next = FIFFV_NEXT_SEQ; - // tag.kind = FIFF_MNE_SOURCE_SPACE_NEIGHBORS; - // tag.type = FIFFT_INT; - // tag.size = ntot*sizeof(fiff_int_t); - // tag.data = (fiff_byte_t *)neighbors; - // if (fiff_write_tag(out,&tag) == FIFF_FAIL) - // goto out; - - /* - * Write some additional stuff - */ - if (!selected_only) { - if (ss->voxel_surf_RAS_t && !ss->voxel_surf_RAS_t->isEmpty()) { - write_coord_trans_old(t_pStream, *ss->voxel_surf_RAS_t);//t_pStream->write_coord_trans(ss->voxel_surf_RAS_t); - - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_VOXEL_DIMS,ss->vol_dims,3); - // tag.next = FIFFV_NEXT_SEQ; - // tag.kind = FIFF_MNE_SOURCE_SPACE_VOXEL_DIMS; - // tag.type = FIFFT_INT; - // tag.size = 3*sizeof(fiff_int_t); - // tag.data = (fiff_byte_t *)ss->vol_dims; - // if (fiff_write_tag(out,&tag) == FIFF_FAIL) - // goto out; - } - if (ss->interpolator && !ss->MRI_volume.isEmpty()) { - t_pStream->start_block(FIFFB_MNE_PARENT_MRI_FILE); - if (ss->MRI_surf_RAS_RAS_t && !ss->MRI_surf_RAS_RAS_t->isEmpty()) - write_coord_trans_old(t_pStream, *ss->MRI_surf_RAS_RAS_t);//t_pStream->write_coord_trans(ss->MRI_surf_RAS_RAS_t); - if (ss->MRI_voxel_surf_RAS_t && !ss->MRI_voxel_surf_RAS_t->isEmpty()) - write_coord_trans_old(t_pStream, *ss->MRI_voxel_surf_RAS_t);//t_pStream->write_coord_trans(ss->MRI_voxel_surf_RAS_t); - t_pStream->write_string(FIFF_MNE_FILE_NAME,ss->MRI_volume); - if (ss->interpolator) - fiff_write_float_sparse_matrix_old(t_pStream,FIFF_MNE_SOURCE_SPACE_INTERPOLATOR,&(*ss->interpolator)); - if (ss->MRI_vol_dims[0] > 0 && ss->MRI_vol_dims[1] > 0 && ss->MRI_vol_dims[2] > 0) { - t_pStream->write_int(FIFF_MRI_WIDTH,&ss->MRI_vol_dims[0]); - t_pStream->write_int(FIFF_MRI_HEIGHT,&ss->MRI_vol_dims[1]); - t_pStream->write_int(FIFF_MRI_DEPTH,&ss->MRI_vol_dims[2]); - } - t_pStream->end_block(FIFFB_MNE_PARENT_MRI_FILE); - } - } - else { - if (ss->interpolator && !ss->MRI_volume.isEmpty()) { - t_pStream->write_string(FIFF_MNE_SOURCE_SPACE_MRI_FILE,ss->MRI_volume); - qCritical("Cannot write the interpolator for selection yet"); - goto out; - } - } - res = OK; - goto out; - -out : { - FREE_41(inuse_map); - FREE_41(nneighbors); - FREE_41(neighbors); - return res; - } -} - -int mne_write_one_source_space(FiffStream::SPtr& t_pStream, MNESourceSpace* ss,bool selected_only) -{ - float **sel = Q_NULLPTR; - int **tris = Q_NULLPTR; - int *nearest = Q_NULLPTR; - float *nearest_dist = Q_NULLPTR; - int p,pp; - - if (ss->np <= 0) { - qCritical("No points in the source space being saved"); - goto bad; - } - - t_pStream->start_block(FIFFB_MNE_SOURCE_SPACE); - - /* - * General information - */ - if (ss->type != FIFFV_MNE_SPACE_UNKNOWN) - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_TYPE,&ss->type); - if (ss->id != FIFFV_MNE_SURF_UNKNOWN) - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_ID,&ss->id); - if (!ss->subject.isEmpty() && ss->subject.size() > 0) { - QString subj(ss->subject); - t_pStream->write_string(FIFF_SUBJ_HIS_ID,subj); - } - - t_pStream->write_int(FIFF_MNE_COORD_FRAME,&ss->coord_frame); - - if (selected_only) { - if (ss->nuse == 0) { - qCritical("No vertices in use. Cannot write active-only vertices from this source space"); - goto bad; - } - sel = ALLOC_CMATRIX_41(ss->nuse,3); - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NPOINTS,&ss->nuse); - for (p = 0, pp = 0; p < ss->np; p++) { - if (ss->inuse[p]) { - sel[pp][X_41] = ss->rr(p,X_41); - sel[pp][Y_41] = ss->rr(p,Y_41); - sel[pp][Z_41] = ss->rr(p,Z_41); - pp++; - } - } - fiff_write_float_matrix_old(t_pStream, FIFF_MNE_SOURCE_SPACE_POINTS, sel,ss->nuse,3); - - for (p = 0, pp = 0; p < ss->np; p++) { - if (ss->inuse[p]) { - sel[pp][X_41] = ss->nn(p,X_41); - sel[pp][Y_41] = ss->nn(p,Y_41); - sel[pp][Z_41] = ss->nn(p,Z_41); - pp++; - } - } - fiff_write_float_matrix_old(t_pStream, FIFF_MNE_SOURCE_SPACE_NORMALS, sel,ss->nuse,3); - FREE_CMATRIX_41(sel); sel = Q_NULLPTR; -#ifdef WRONG - /* - * This code is incorrect because the numbering in the nuse triangulation refers to the complete source space - */ - if (ss->nuse_tri > 0) { /* Write the triangulation information */ - /* - * The 'use' triangulation is identical to the complete one - */ - if (fiff_write_int_tag(out,FIFF_MNE_SOURCE_SPACE_NTRI,ss->nuse_tri) == FIFF_FAIL) - goto bad; - tris = make_file_triangle_list(ss->use_itris,ss->nuse_tri); - if (fiff_write_int_matrix(out,FIFF_MNE_SOURCE_SPACE_TRIANGLES,tris, - ss->nuse_tri,3) == FIFF_FAIL) - goto bad; - - if (fiff_write_int_tag(out,FIFF_MNE_SOURCE_SPACE_NUSE_TRI,ss->nuse_tri) == FIFF_FAIL) - goto bad; - if (fiff_write_int_matrix(out,FIFF_MNE_SOURCE_SPACE_USE_TRIANGLES,tris, - ss->nuse_tri,3) == FIFF_FAIL) - goto bad; - FREE_ICMATRIX(tris); tris = Q_NULLPTR; - } -#endif - } - else { - // fiffTagRec tag; - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NPOINTS,&ss->np); - - t_pStream->write_float_matrix(FIFF_MNE_SOURCE_SPACE_POINTS, Eigen::MatrixXf(ss->rr)); - - t_pStream->write_float_matrix(FIFF_MNE_SOURCE_SPACE_NORMALS, Eigen::MatrixXf(ss->nn)); - - if (ss->nuse > 0 && ss->inuse.size() > 0) { - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_SELECTION,ss->inuse.data(),ss->np); - // tag.next = 0; - // tag.kind = FIFF_MNE_SOURCE_SPACE_SELECTION; - // tag.type = FIFFT_INT; - // tag.size = (ss->np)*sizeof(fiff_int_t); - // tag.data = (fiff_byte_t *)(ss->inuse); - // if (fiff_write_tag(out,&tag) == FIFF_FAIL) - // goto bad; - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NUSE,&ss->nuse); - // if (fiff_write_int_tag (out, FIFF_MNE_SOURCE_SPACE_NUSE,ss->nuse) == FIFF_FAIL) - // goto bad; - } - if (ss->ntri > 0) { /* Write the triangulation information */ - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NTRI,&ss->ntri); - // if (fiff_write_int_tag(out,FIFF_MNE_SOURCE_SPACE_NTRI,ss->ntri) == FIFF_FAIL) - // goto bad; - Eigen::MatrixXi file_tris = ss->itris.array() + 1; - t_pStream->write_int_matrix(FIFF_MNE_SOURCE_SPACE_TRIANGLES, file_tris); - // if (fiff_write_int_matrix(out,FIFF_MNE_SOURCE_SPACE_TRIANGLES,tris, - // ss->ntri,3) == FIFF_FAIL) - // goto bad; - } - if (ss->nuse_tri > 0) { /* Write the triangulation information for the vertices in use */ - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NUSE_TRI,&ss->nuse_tri); - // if (fiff_write_int_tag(out,FIFF_MNE_SOURCE_SPACE_NUSE_TRI,ss->nuse_tri) == FIFF_FAIL) - // goto bad; - Eigen::MatrixXi file_use_tris = ss->use_itris.array() + 1; - t_pStream->write_int_matrix(FIFF_MNE_SOURCE_SPACE_USE_TRIANGLES, file_use_tris); - // if (fiff_write_int_matrix(out,FIFF_MNE_SOURCE_SPACE_USE_TRIANGLES,tris, - // ss->nuse_tri,3) == FIFF_FAIL) - // goto bad; - } - if (!ss->nearest.empty()) { /* Write the patch information */ - nearest = MALLOC_41(ss->np,int); - nearest_dist = MALLOC_41(ss->np,float); - - std::sort(ss->nearest.begin(), ss->nearest.end(), - [](const MNENearest& a, const MNENearest& b) { return a.vert < b.vert; }); - for (p = 0; p < ss->np; p++) { - nearest[p] = ss->nearest[p].nearest; - nearest_dist[p] = ss->nearest[p].dist; - } - - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NEAREST,nearest,ss->np); - // tag.next = FIFFV_NEXT_SEQ; - // tag.kind = FIFF_MNE_SOURCE_SPACE_NEAREST; - // tag.type = FIFFT_INT; - // tag.size = (ss->np)*sizeof(fiff_int_t); - // tag.data = (fiff_byte_t *)(nearest); - // if (fiff_write_tag(out,&tag) == FIFF_FAIL) - // goto bad; - - t_pStream->write_float(FIFF_MNE_SOURCE_SPACE_NEAREST_DIST,nearest_dist,ss->np); - // tag.next = FIFFV_NEXT_SEQ; - // tag.kind = FIFF_MNE_SOURCE_SPACE_NEAREST_DIST; - // tag.type = FIFFT_FLOAT; - // tag.size = (ss->np)*sizeof(fiff_float_t); - // tag.data = (fiff_byte_t *)(nearest_dist); - // if (fiff_write_tag(out,&tag) == FIFF_FAIL) - // goto bad; - - FREE_41(nearest); nearest = Q_NULLPTR; - FREE_41(nearest_dist); nearest_dist = Q_NULLPTR; - } - if (!ss->dist.is_empty()) { /* Distance information */ - FiffSparseMatrix* m = mne_pick_lower_triangle_rcs(&ss->dist); - if (!m) - goto bad; - if (fiff_write_float_sparse_matrix_old(t_pStream,FIFF_MNE_SOURCE_SPACE_DIST,m) == FIFF_FAIL) { - if(m) - delete m; - goto bad; - } - if(m) - delete m; - - t_pStream->write_float(FIFF_MNE_SOURCE_SPACE_DIST_LIMIT,&ss->dist_limit); - } - } - /* - * Volume source spaces have additional information - */ - // if (write_volume_space_info(out,ss,selected_only) == FIFF_FAIL) - // goto bad; - - t_pStream->end_block(FIFFB_MNE_SOURCE_SPACE); - return FIFF_OK; - -bad : { - FREE_ICMATRIX_41(tris); - FREE_CMATRIX_41(sel); - FREE_41(nearest); - FREE_41(nearest_dist); - return FIFF_FAIL; - } -} - -QString mne_name_list_to_string_41(const QStringList& list) -/* - * Convert a string array to a colon-separated string - */ -{ - int nlist = list.size(); - QString res; - if (nlist == 0 || list.isEmpty()) - return res; -// res[0] = '\0'; - for (int k = 0; k < nlist-1; k++) { - res += list[k]; - res += ":"; - } - res += list[nlist-1]; - return res; -} - -int mne_write_named_matrix( FiffStream::SPtr& t_pStream, - int kind, - MNENamedMatrix* mat) -/* - * Write a block which contains information about one named matrix - */ -{ - QString names; - - t_pStream->start_block(FIFFB_MNE_NAMED_MATRIX); - - t_pStream->write_int(FIFF_MNE_NROW,&mat->nrow); - t_pStream->write_int(FIFF_MNE_NCOL,&mat->ncol); - if (!mat->rowlist.isEmpty()) { - names = mne_name_list_to_string_41(mat->rowlist); - t_pStream->write_string(FIFF_MNE_ROW_NAMES,names); - } - if (!mat->collist.isEmpty()) { - names = mne_name_list_to_string_41(mat->collist); - t_pStream->write_string(FIFF_MNE_COL_NAMES,names); - } - t_pStream->write_float_matrix(kind, mat->data); - - t_pStream->end_block(FIFFB_MNE_NAMED_MATRIX); - - return FIFF_OK; -} - -bool fiff_put_dir (FiffStream::SPtr& t_pStream, const QList& dir) -/* - * Put in new directory - */ -{ - int nent = dir.size(); - int k; - FiffTag::SPtr t_pTag; - fiff_int_t dirpos; - - for (k = 0; k < nent; k++) { - if (dir[k]->kind == FIFF_DIR_POINTER) { - /* - * Read current value of directory pointer - */ - if (!t_pStream->read_tag(t_pTag,dir[k]->pos)) { - fprintf (stderr,"Could not read FIFF_DIR_POINTER!\n"); - return false; - } - /* - * If there is no directory, append the new one - */ - dirpos = *t_pTag->toInt();//2GB restriction - if (dirpos <= 0) - dirpos = -1; - /* - * Put together the directory tag - */ - dirpos = (fiff_int_t)t_pStream->write_dir_entries(dir);//2GB restriction - if (dirpos < 0) - printf ("Could not update directory!\n"); - else { - t_pTag->setNum(dirpos); -// t_pStream->write_tag(t_pTag,dir[k]->pos); - - t_pStream->write_dir_pointer(dirpos, dir[k]->pos); - -// t_pStream->device()->seek(dir[k]->pos); - -// fiff_int_t datasize = 1 * 4; - -// *t_pStream << (qint32)t_pTag->kind; -// *t_pStream << (qint32)t_pTag->type; -// *t_pStream << (qint32)datasize; -// *t_pStream << (qint32)t_pTag->next; - -// *t_pStream << dirpos; - - } - return true; - } - } - printf ("Could not find place for directory!\n"); - return false; -} - -//============================= write_solution.c ============================= - -bool write_solution(const QString& name, /* Destination file */ - std::vector>& spaces, /* The source spaces */ - const QString& mri_file, /* MRI file and data obtained from there */ - fiffId mri_id, - const FiffCoordTrans& mri_head_t, - const QString& meas_file, /* MEG file and data obtained from there */ - FiffId meas_id, - const FiffCoordTrans& meg_head_t, - QList meg_chs, - int nmeg, - QList eeg_chs, - int neeg, - int fixed_ori, /* Fixed orientation dipoles? */ - int coord_frame, /* Coordinate frame employed in the forward calculations */ - FiffNamedMatrix& meg_solution, - FiffNamedMatrix& eeg_solution, - FiffNamedMatrix& meg_solution_grad, - FiffNamedMatrix& eeg_solution_grad, - bool bDoGrad) -{ - // New Stuff - QFile file(name); - - QFile fileIn(name); - FiffStream::SPtr t_pStreamIn; - - int nvert; - int k; - - // - // Open the file, create directory - // - - // Create the file and save the essentials - FiffStream::SPtr t_pStream = FiffStream::start_file(file); - - t_pStream->start_block(FIFFB_MNE); - - /* - * Information from the MRI file - */ - { - t_pStream->start_block(FIFFB_MNE_PARENT_MRI_FILE); - - t_pStream->write_string(FIFF_MNE_FILE_NAME, mri_file); - if (mri_id != Q_NULLPTR) - write_id_old(t_pStream, FIFF_PARENT_FILE_ID, mri_id);//t_pStream->write_id(FIFF_PARENT_FILE_ID, mri_id); - write_coord_trans_old(t_pStream, mri_head_t);//t_pStream->write_coord_trans(mri_head_t); - - t_pStream->end_block(FIFFB_MNE_PARENT_MRI_FILE); - } - - /* - * Information from the MEG file - */ - { - QStringList file_bads; - - t_pStream->start_block(FIFFB_MNE_PARENT_MEAS_FILE); - - t_pStream->write_string(FIFF_MNE_FILE_NAME, meas_file); - if (!meas_id.isEmpty()) { - t_pStream->write_id(FIFF_PARENT_BLOCK_ID, meas_id); - } - write_coord_trans_old(t_pStream, meg_head_t);//t_pStream->write_coord_trans(meg_head_t); - - int nchan = nmeg+neeg; - t_pStream->write_int(FIFF_NCHAN,&nchan); - - int k, p; - for (k = 0, p = 0; k < nmeg; k++) { - meg_chs[k].scanNo = ++p; - t_pStream->write_ch_info(meg_chs[k]); - } - - for (k = 0; k < neeg; k++) { - eeg_chs[k].scanNo = ++p; - if (k == 0) fprintf(stderr, "[write_solution] EEG[0] scanNo=%d, p=%d, nmeg=%d, neeg=%d\n", eeg_chs[k].scanNo, p, nmeg, neeg); - t_pStream->write_ch_info(eeg_chs[k]); - } - fprintf(stderr, "[write_solution] after loops: nmeg=%d, neeg=%d, p=%d, meg_chs.size=%lld, eeg_chs.size=%lld\n", nmeg, neeg, p, (long long)meg_chs.size(), (long long)eeg_chs.size()); - /* - * Copy the bad channel list from the measurement file - */ - - // - // mne_read_bad_channel_list replacement - // - QFile fileBad(meas_file); - FiffStream::SPtr t_pStreamBads(new FiffStream(&fileBad)); - if(!t_pStreamBads->open()) - return false; - file_bads = t_pStreamBads->read_bad_channels(t_pStreamBads->dirtree()); - - // - // mne_write_bad_channel_list replacement - // - mne_write_bad_channel_list_new(t_pStream,file_bads); - - t_pStream->end_block(FIFFB_MNE_PARENT_MEAS_FILE); - } - - /* - * Write the source spaces (again) - */ - for (k = 0, nvert = 0; k < static_cast(spaces.size()); k++) { - if (mne_write_one_source_space(t_pStream,spaces[k].get(),FALSE) == FIFF_FAIL) - goto bad; - nvert += spaces[k]->nuse; - } - - /* - * MEG forward solution - */ - if (nmeg > 0) { - t_pStream->start_block(FIFFB_MNE_FORWARD_SOLUTION); - - int val = FIFFV_MNE_MEG; - t_pStream->write_int(FIFF_MNE_INCLUDED_METHODS,&val); - t_pStream->write_int(FIFF_MNE_COORD_FRAME,&coord_frame); - val = fixed_ori ? FIFFV_MNE_FIXED_ORI : FIFFV_MNE_FREE_ORI; - t_pStream->write_int(FIFF_MNE_SOURCE_ORIENTATION,&val); - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NPOINTS,&nvert); - t_pStream->write_int(FIFF_NCHAN,&nmeg); - - meg_solution.transpose_named_matrix(); - t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION,meg_solution); - meg_solution.transpose_named_matrix(); -// if (mne_write_named_matrix(t_pStream,FIFF_MNE_FORWARD_SOLUTION,meg_solution) == FIFF_FAIL) -// goto bad; - if (bDoGrad) { - meg_solution_grad.transpose_named_matrix(); - t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION_GRAD,meg_solution_grad); - meg_solution_grad.transpose_named_matrix(); - } - -// if (mne_write_named_matrix(t_pStream,FIFF_MNE_FORWARD_SOLUTION_GRAD,meg_solution_grad) == FIFF_FAIL) -// goto bad; - t_pStream->end_block(FIFFB_MNE_FORWARD_SOLUTION); - } - /* - * EEG forward solution - */ - if (neeg > 0) { - t_pStream->start_block(FIFFB_MNE_FORWARD_SOLUTION); - - int val = FIFFV_MNE_EEG; - t_pStream->write_int(FIFF_MNE_INCLUDED_METHODS,&val); - t_pStream->write_int(FIFF_MNE_COORD_FRAME,&coord_frame); - val = fixed_ori ? FIFFV_MNE_FIXED_ORI : FIFFV_MNE_FREE_ORI; - t_pStream->write_int(FIFF_MNE_SOURCE_ORIENTATION,&val); - t_pStream->write_int(FIFF_NCHAN,&neeg); - t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NPOINTS,&nvert); - - eeg_solution.transpose_named_matrix(); - t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION,eeg_solution); -// if (mne_write_named_matrix(t_pStream,FIFF_MNE_FORWARD_SOLUTION,eeg_solution) == FIFF_FAIL) - eeg_solution.transpose_named_matrix(); -// goto bad; - if (bDoGrad) { - eeg_solution_grad.transpose_named_matrix(); - t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION_GRAD,eeg_solution_grad); - eeg_solution_grad.transpose_named_matrix(); - } -// if (mne_write_named_matrix(t_pStream,FIFF_MNE_FORWARD_SOLUTION_GRAD,eeg_solution_grad) == FIFF_FAIL) -// goto bad; - - t_pStream->end_block(FIFFB_MNE_FORWARD_SOLUTION); - } - - t_pStream->end_block(FIFFB_MNE); - t_pStream->end_file(); - t_pStream->close(); - t_pStream.clear(); - - /* - * Add directory - */ - t_pStreamIn = FiffStream::open_update(fileIn); - - if (!fiff_put_dir(t_pStreamIn,t_pStreamIn->dir())) - goto bad; - if(t_pStreamIn) - t_pStreamIn->close(); - - return true; - -bad : { - if(t_pStream) - t_pStream->close(); - if(t_pStreamIn) - t_pStreamIn->close(); - return false; - } -} - -/* - * Process the environment information - */ - -bool mne_attach_env(const QString& name, const QString& command) -/* - * Add the environment info for future reference - */ -{ - int insert_blocks[] = { FIFFB_MNE , FIFFB_MEAS, FIFFB_MRI, FIFFB_BEM, -1 }; - QString cwd = QDir::currentPath(); - - int b,k, insert; - FiffTag::SPtr t_pTag; -// QList tags; - QFile fileInOut(name); - FiffStream::SPtr t_pStreamInOut; - -// if (fiff_new_file_id(&id) == FIFF_FAIL) -// return false; - FiffId id(FiffId::new_file_id()); - -//#ifdef DEBUG -// printf("\n"); -// printf("cwd = %s\n",cwd); -// printf("com = %s\n",command); -// printf("envid = %s\n",mne_format_file_id(&id)); -//#endif - - if (!fileInOut.exists()) { - qCritical("File %s does not exist. Cannot attach env info.",name.toUtf8().constData()); - return false; - } -// if (!fileInOut.isWritable()) { -// qCritical("File %s is not writable. Cannot attach env info.",name.toUtf8().constData()); -// return false; -// } - /* - * Open the file to modify - */ - if (!(t_pStreamInOut = FiffStream::open_update(fileInOut))) - return false; - /* - * Find an appropriate position to insert - */ - for (insert = -1, b = 0; insert_blocks[b] >= 0; b++) { - for (k = 0; k < t_pStreamInOut->nent(); k++) { - if (t_pStreamInOut->dir()[k]->kind == FIFF_BLOCK_START) { - if (!t_pStreamInOut->read_tag(t_pTag, t_pStreamInOut->dir()[k]->pos)) - return false; - if (*(t_pTag->toInt()) == insert_blocks[b]) { - insert = k; - break; - } - } - } - if (insert >= 0) - break; - } - if (insert < 0) { - qCritical("Suitable place for environment insertion not found."); - return false; - } - - /* - * Do not build the list of tags to insert -> Do insertion right away - */ - - // Modified of fiff_insert_after - int where = insert; - /* - * Insert new tags into a file - * The directory information in dest is updated - */ - if (where < 0 || where >= t_pStreamInOut->nent()-1) { - qCritical("illegal insertion point in fiff_insert_after!"); - return false; - } - - FiffTag::SPtr t_pTagNext; - QList old_dir = t_pStreamInOut->dir(); - QList this_ent = old_dir.mid(where);//this_ent = old_dir + where; - - if (!t_pStreamInOut->read_tag(t_pTagNext, this_ent[0]->pos)) - return false; - /* - * Update next info to be sequential - */ - qint64 next_tmp = t_pStreamInOut->device()->pos(); - /* - * Go to the end of the file - */ - t_pStreamInOut->device()->seek(fileInOut.size());//SEEK_END - /* - * Allocate new directory - * Copy the beginning of old directory - */ - QList new_dir = old_dir.mid(0,where+1); - - /* - * Save the old size for future purposes - */ - qint64 old_end = t_pStreamInOut->device()->pos(); - /* - * Write tags, check for errors - */ - - //Don't use the for loop here instead do it explicitly for specific tags - FiffDirEntry::SPtr new_this; - - new_this = FiffDirEntry::SPtr(new FiffDirEntry); - new_this->kind = FIFF_BLOCK_START; - new_this->type = FIFFT_INT; - new_this->size = 1 * 4; - new_this->pos = t_pStreamInOut->start_block(FIFFB_MNE_ENV); - new_dir.append(new_this); - - new_this = FiffDirEntry::SPtr(new FiffDirEntry); - new_this->kind = FIFF_BLOCK_ID; - new_this->type = FIFFT_ID_STRUCT; - new_this->size = 5 * 4; - new_this->pos = t_pStreamInOut->write_id(FIFF_BLOCK_ID,id); - new_dir.append(new_this); - - new_this = FiffDirEntry::SPtr(new FiffDirEntry); - new_this->kind = FIFF_MNE_ENV_WORKING_DIR; - new_this->type = FIFFT_STRING; - new_this->size = cwd.size(); - new_this->pos = t_pStreamInOut->write_string(FIFF_MNE_ENV_WORKING_DIR,cwd); - new_dir.append(new_this); - - new_this = FiffDirEntry::SPtr(new FiffDirEntry); - new_this->kind = FIFF_MNE_ENV_COMMAND_LINE; - new_this->type = FIFFT_STRING; - new_this->size = command.size(); - new_this->pos = t_pStreamInOut->write_string(FIFF_MNE_ENV_COMMAND_LINE,command); - new_dir.append(new_this); - - new_this = FiffDirEntry::SPtr(new FiffDirEntry); - new_this->kind = FIFF_BLOCK_END; - new_this->type = FIFFT_INT; - new_this->size = 1 * 4; - - new_this->pos = t_pStreamInOut->end_block(FIFFB_MNE_ENV,next_tmp); - new_dir.append(new_this); - - /* - * Copy the rest of the old directory - */ - new_dir.append(old_dir.mid(where+1)); - /* - * Now, it is time to update the braching tag - * If something goes wrong here, we cannot be sure that - * the file is readable. Let's hope for the best... - */ - t_pTagNext->next = (qint32)old_end;//2GB cut of - t_pStreamInOut->write_tag(t_pTagNext,this_ent[0]->pos); - - /* - * Update - */ - t_pStreamInOut->dir() = new_dir; - - // Finished fiff_insert_after - - return true; -} - -//============================================================================================================= -// STATIC DEFINITIONS -//============================================================================================================= - -//============================================================================================================= -// DEFINE MEMBER METHODS -//============================================================================================================= - -ComputeFwd::ComputeFwd(ComputeFwdSettings::SPtr pSettings) - : sol(new FiffNamedMatrix) - , sol_grad(new FiffNamedMatrix) - , m_meg_forward(new FiffNamedMatrix) - , m_meg_forward_grad(new FiffNamedMatrix) - , m_eeg_forward(new FiffNamedMatrix) - , m_eeg_forward_grad(new FiffNamedMatrix) - , m_pSettings(pSettings) -{ - initFwd(); -} - -//============================================================================================================= - -ComputeFwd::~ComputeFwd() -{ - //ToDo Garbage collection - if(m_megcoils) - delete m_megcoils; - if(m_eegels) - delete m_eegels; - if(m_eegModel) - delete m_eegModel; -} - -//============================================================================================================= - -void ComputeFwd::initFwd() -{ - // TODO: This only temporary until we have the fwd dlibrary refactored. This is only done in order to provide easy testing in test_forward_solution. - m_spaces.clear(); - m_iNSource = 0; - - m_mri_head_t = FiffCoordTrans(); - m_meg_head_t = FiffCoordTrans(); - - m_listMegChs = QList(); - m_listEegChs = QList(); - m_listCompChs = QList(); - - int iNMeg = 0; - int iNEeg = 0; - int iNComp = 0; - - m_templates = Q_NULLPTR; - m_megcoils = Q_NULLPTR; - m_compcoils = Q_NULLPTR; - m_eegels = Q_NULLPTR; - m_eegModels = Q_NULLPTR; - m_iNChan = 0; - - int k; - m_mri_id = Q_NULLPTR; - m_meas_id.clear(); - - QFile filteredFile; /**< Output filtered points here. */ - QTextStream *filteredStream = Q_NULLPTR; /**< Text stream for filtered output. */ - - m_eegModel = Q_NULLPTR; - m_bemModel = Q_NULLPTR; - - // Report the setup - - // printf("\n"); - // mne_print_version_info(stderr,argv[0],PROGRAM_VERSION,__DATE__,__TIME__); - printf("\n"); - printf("Source space : %s\n",m_pSettings->srcname.toUtf8().constData()); - if (!(m_pSettings->transname.isEmpty()) || !(m_pSettings->mriname.isEmpty())) { - printf("MRI -> head transform source : %s\n",!(m_pSettings->mriname.isEmpty()) ? m_pSettings->mriname.toUtf8().constData() : m_pSettings->transname.toUtf8().constData()); - } else { - printf("MRI and head coordinates are assumed to be identical.\n"); - } - printf("Measurement data : %s\n",m_pSettings->measname.toUtf8().constData()); - if (!m_pSettings->bemname.isEmpty()) { - printf("BEM model : %s\n",m_pSettings->bemname.toUtf8().constData()); - } else { - printf("Sphere model : origin at (% 7.2f % 7.2f % 7.2f) mm\n", - 1000.0f*m_pSettings->r0[X_41],1000.0f*m_pSettings->r0[Y_41],1000.0f*m_pSettings->r0[Z_41]); - if (m_pSettings->include_eeg) { - printf("\n"); - - if (m_pSettings->eeg_model_file.isEmpty()) { - qCritical("!!!!!!!!!!TODO: default_eeg_model_file();"); - // m_pSettings->eeg_model_file = default_eeg_model_file(); - } - m_eegModels = FwdEegSphereModelSet::fwd_load_eeg_sphere_models(m_pSettings->eeg_model_file,m_eegModels); - m_eegModels->fwd_list_eeg_sphere_models(stderr); - - if (m_pSettings->eeg_model_name.isEmpty()) { - m_pSettings->eeg_model_name = QString("Default"); - } - if ((m_eegModel = m_eegModels->fwd_select_eeg_sphere_model(m_pSettings->eeg_model_name)) == Q_NULLPTR) { - return; - } - - if (!m_eegModel->fwd_setup_eeg_sphere_model(m_pSettings->eeg_sphere_rad,m_pSettings->use_equiv_eeg,3)) { - return; - } - - printf("Using EEG sphere model \"%s\" with scalp radius %7.1f mm\n", - m_pSettings->eeg_model_name.toUtf8().constData(),1000*m_pSettings->eeg_sphere_rad); - printf("%s the electrode locations to scalp\n",m_pSettings->scale_eeg_pos ? "Scale" : "Do not scale"); - - m_eegModel->scale_pos = m_pSettings->scale_eeg_pos; - VEC_COPY_41(m_eegModel->r0,m_pSettings->r0); - - printf("\n"); - } - } - printf("%s field computations\n",m_pSettings->accurate ? "Accurate" : "Standard"); - printf("Do computations in %s coordinates.\n",FiffCoordTrans::frame_name(m_pSettings->coord_frame).toUtf8().constData()); - printf("%s source orientations\n",m_pSettings->fixed_ori ? "Fixed" : "Free"); - if (m_pSettings->compute_grad) { - printf("Compute derivatives with respect to source location coordinates\n"); - } - printf("Destination for the solution : %s\n",m_pSettings->solname.toUtf8().constData()); - if (m_pSettings->do_all) { - printf("Calculate solution for all source locations.\n"); - } - if (m_pSettings->nlabel > 0) { - printf("Source space will be restricted to sources in %d labels\n",m_pSettings->nlabel); - } - - // Read the source locations - - printf("\n"); - printf("Reading %s...\n",m_pSettings->srcname.toUtf8().constData()); - if (MNESourceSpace::read_source_spaces(m_pSettings->srcname,m_spaces) != OK) { - return; - } - for (k = 0, m_iNSource = 0; k < static_cast(m_spaces.size()); k++) { - if (m_pSettings->do_all) { - m_spaces[k]->enable_all_sources(); - } - m_iNSource += m_spaces[k]->nuse; - } - if (m_iNSource == 0) { - qCritical("No sources are active in these source spaces. --all option should be used."); - return; - } - printf("Read %d source spaces a total of %d active source locations\n", static_cast(m_spaces.size()),m_iNSource); - if (MNESourceSpace::restrict_sources_to_labels(m_spaces,m_pSettings->labels,m_pSettings->nlabel) == FAIL) { - return; - } - - // Read the MRI -> head coordinate transformation - printf("\n"); - if (!m_pSettings->mriname.isEmpty()) { - m_mri_head_t = FiffCoordTrans::readMriTransform(m_pSettings->mriname); - if (m_mri_head_t.isEmpty()) { - return; - } - if ((m_mri_id = get_file_id(m_pSettings->mriname)) == Q_NULLPTR) { - qCritical("Couln't read MRI file id (How come?)"); - return; - } - } - else if (!m_pSettings->transname.isEmpty()) { - FiffCoordTrans t = FiffCoordTrans::readFShead2mriTransform(m_pSettings->transname.toUtf8().data()); - if (t.isEmpty()) { - return; - } - m_mri_head_t = t.inverted(); - } else { - m_mri_head_t = FiffCoordTrans::identity(FIFFV_COORD_MRI,FIFFV_COORD_HEAD); - } - m_mri_head_t.print(); - - // Read the channel information and the MEG device -> head coordinate transformation - // replace mne_read_meg_comp_eeg_ch_info_41() - - if(!m_pSettings->pFiffInfo) { - - QFile measname(m_pSettings->measname); - FIFFLIB::FiffDirNode::SPtr DirNode; - FiffStream::SPtr pStream(new FiffStream(&measname)); - FIFFLIB::FiffInfo fiffInfo; - if(!pStream->open()) { - qCritical() << "Could not open Stream."; - return; - } - - //Get Fiff info - if(!pStream->read_meas_info(pStream->dirtree(), fiffInfo, DirNode)){ - qCritical() << "Could not find the channel information."; - return; - } - pStream->close(); - m_pInfoBase = QSharedPointer(new FiffInfo(fiffInfo)); - } else { - m_pInfoBase = m_pSettings->pFiffInfo; - } - if(!m_pInfoBase) { - qCritical ("ComputeFwd::initFwd(): no FiffInfo"); - return; - } - if (mne_read_meg_comp_eeg_ch_info_41(m_pInfoBase, - m_listMegChs, - iNMeg, - m_listCompChs, - iNComp, - m_listEegChs, - iNEeg, - m_meg_head_t, - m_meas_id) != OK) { - return; - } - - m_iNChan = iNMeg + iNEeg; - - printf("\n"); - if (iNMeg > 0) { - printf("Read %3d MEG channels from %s\n",iNMeg,m_pSettings->measname.toUtf8().constData()); - } - if (iNComp > 0) { - printf("Read %3d MEG compensation channels from %s\n",iNComp,m_pSettings->measname.toUtf8().constData()); - } - if (iNEeg > 0) { - printf("Read %3d EEG channels from %s\n",iNEeg,m_pSettings->measname.toUtf8().constData()); - } - if (!m_pSettings->include_meg) { - printf("MEG not requested. MEG channels omitted.\n"); - m_listMegChs.clear(); - m_listCompChs.clear(); - iNMeg = 0; - iNComp = 0; - } - else - m_meg_head_t.print(); - if (!m_pSettings->include_eeg) { - printf("EEG not requested. EEG channels omitted.\n"); - m_listEegChs.clear(); - iNEeg = 0; - } else { - if (mne_check_chinfo(m_listEegChs,iNEeg) != OK) { - return; - } - } - - // Create coil descriptions with transformation to head or MRI frame - - if (m_pSettings->include_meg) { - qPath = QString(QCoreApplication::applicationDirPath() + "/../resources/general/coilDefinitions/coil_def.dat"); - file.setFileName(qPath); - if ( !QCoreApplication::startingUp() ) { - qPath = QCoreApplication::applicationDirPath() + QString("/../resources/general/coilDefinitions/coil_def.dat"); - } else if (!file.exists()) { - qPath = "../resources/general/coilDefinitions/coil_def.dat"; - } - - m_templates = FwdCoilSet::read_coil_defs(qPath); - if (!m_templates) { - return; - } - - // Compensation data - - m_compData = MNECTFCompDataSet::read(m_pSettings->measname); - if (!m_compData) { - return; - } - // Compensation channel information may be needed - if (m_compData->ncomp > 0) { - printf("%d compensation data sets in %s\n",m_compData->ncomp,m_pSettings->measname.toUtf8().constData()); - } else { - m_listCompChs.clear(); - iNComp = 0; - - m_compData.reset(); - } - } - if (m_pSettings->coord_frame == FIFFV_COORD_MRI) { - FiffCoordTrans head_mri_t = m_mri_head_t.inverted(); - FiffCoordTrans meg_mri_t = FiffCoordTrans::combine(FIFFV_COORD_DEVICE,FIFFV_COORD_MRI,m_meg_head_t,head_mri_t); - if (meg_mri_t.isEmpty()) { - return; - } - if ((m_megcoils = m_templates->create_meg_coils(m_listMegChs, - iNMeg, - m_pSettings->accurate ? FWD_COIL_ACCURACY_ACCURATE : FWD_COIL_ACCURACY_NORMAL, - meg_mri_t)) == Q_NULLPTR) { - return; - } - if (iNComp > 0) { - if ((m_compcoils = m_templates->create_meg_coils(m_listCompChs, - iNComp, - FWD_COIL_ACCURACY_NORMAL, - meg_mri_t)) == Q_NULLPTR) { - return; - } - } - if ((m_eegels = FwdCoilSet::create_eeg_els(m_listEegChs, - iNEeg, - head_mri_t)) == Q_NULLPTR) { - return; - } - - printf("MRI coordinate coil definitions created.\n"); - } else { - if ((m_megcoils = m_templates->create_meg_coils(m_listMegChs, - iNMeg, - m_pSettings->accurate ? FWD_COIL_ACCURACY_ACCURATE : FWD_COIL_ACCURACY_NORMAL, - m_meg_head_t)) == Q_NULLPTR) { - return; - } - - if (iNComp > 0) { - if ((m_compcoils = m_templates->create_meg_coils(m_listCompChs, - iNComp, - FWD_COIL_ACCURACY_NORMAL,m_meg_head_t)) == Q_NULLPTR) { - return; - } - } - if ((m_eegels = FwdCoilSet::create_eeg_els(m_listEegChs, - iNEeg)) == Q_NULLPTR) { - return; - } - printf("Head coordinate coil definitions created.\n"); - } - - // Transform the source spaces into the appropriate coordinates - { - if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame,m_mri_head_t,m_spaces) != OK) { - return; - } - } - printf("Source spaces are now in %s coordinates.\n",FiffCoordTrans::frame_name(m_pSettings->coord_frame).toUtf8().constData()); - - // Prepare the BEM model if necessary - - if (!m_pSettings->bemname.isEmpty()) { - QString bemsolname = FwdBemModel::fwd_bem_make_bem_sol_name(m_pSettings->bemname); - // FREE(bemname); - m_pSettings->bemname = bemsolname; - - printf("\nSetting up the BEM model using %s...\n",m_pSettings->bemname.toUtf8().constData()); - printf("\nLoading surfaces...\n"); - m_bemModel = FwdBemModel::fwd_bem_load_three_layer_surfaces(m_pSettings->bemname); - - if (m_bemModel) { - printf("Three-layer model surfaces loaded.\n"); - } - else { - m_bemModel = FwdBemModel::fwd_bem_load_homog_surface(m_pSettings->bemname); - if (!m_bemModel) { - return; - } - printf("Homogeneous model surface loaded.\n"); - } - if (iNEeg > 0 && m_bemModel->nsurf == 1) { - qCritical("Cannot use a homogeneous model in EEG calculations."); - return; - } - printf("\nLoading the solution matrix...\n"); - if (FwdBemModel::fwd_bem_load_recompute_solution(m_pSettings->bemname.toUtf8().data(),FWD_BEM_UNKNOWN,FALSE,m_bemModel) == FAIL) { - return; - } - if (m_pSettings->coord_frame == FIFFV_COORD_HEAD) { - printf("Employing the head->MRI coordinate transform with the BEM model.\n"); - if (FwdBemModel::fwd_bem_set_head_mri_t(m_bemModel,m_mri_head_t) == FAIL) { - return; - } - } - printf("BEM model %s is now set up\n",m_bemModel->sol_name.toUtf8().constData()); - } else { - printf("Using the sphere model.\n"); - } - printf ("\n"); - - // Try to circumvent numerical problems by excluding points too close our ouside the inner skull surface - - if (m_pSettings->filter_spaces) { - if (!m_pSettings->mindistoutname.isEmpty()) { - filteredFile.setFileName(m_pSettings->mindistoutname); - if (!filteredFile.open(QIODevice::WriteOnly | QIODevice::Text)) { - qCritical() << m_pSettings->mindistoutname; - return; - } - filteredStream = new QTextStream(&filteredFile); - printf("Omitted source space points will be output to : %s\n",m_pSettings->mindistoutname.toUtf8().constData()); - } - MNESourceSpace::filter_source_spaces(m_pSettings->mindist, - m_pSettings->bemname, - m_mri_head_t, - m_spaces, - filteredStream,m_pSettings->use_threads); - delete filteredStream; - filteredStream = Q_NULLPTR; - } -} - -//========================================================================================================= - -void ComputeFwd::calculateFwd() -{ - int iNMeg = 0; - int iNEeg = 0; - - if(m_megcoils) { - iNMeg = m_megcoils->ncoil; - } - if(m_eegels) { - iNEeg = m_eegels->ncoil; - } - if (!m_bemModel) { - m_pSettings->use_threads = false; - } - - // check if source spaces are still in head space - if(m_spaces[0]->coord_frame != FIFFV_COORD_HEAD) { - if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame,m_mri_head_t,m_spaces) != OK) { - return; - } - } - - // Do the actual computation - if (iNMeg > 0) { - if ((FwdBemModel::compute_forward_meg(m_spaces, - m_megcoils, - m_compcoils, - m_compData.get(), - m_pSettings->fixed_ori, - m_bemModel, - &m_pSettings->r0, - m_pSettings->use_threads, - *m_meg_forward.data(), - *m_meg_forward_grad.data(), - m_pSettings->compute_grad)) == FAIL) { - return; - } - } - if (iNEeg > 0) { - if ((FwdBemModel::compute_forward_eeg(m_spaces, - m_eegels, - m_pSettings->fixed_ori, - m_bemModel, - m_eegModel, - m_pSettings->use_threads, - *m_eeg_forward.data(), - *m_eeg_forward_grad.data(), - m_pSettings->compute_grad))== FAIL) { - return; - } - } - if(iNMeg > 0 && iNEeg > 0) { - if(m_meg_forward->data.cols() != m_eeg_forward->data.cols()) { - qWarning() << "The MEG and EEG forward solutions do not match"; - return; - } - sol->clear(); - sol->data = MatrixXd(m_meg_forward->nrow + m_eeg_forward->nrow, m_meg_forward->ncol); - sol->data.block(0,0,m_meg_forward->nrow,m_meg_forward->ncol) = m_meg_forward->data; - sol->data.block(m_meg_forward->nrow,0,m_eeg_forward->nrow,m_eeg_forward->ncol) = m_eeg_forward->data; - sol->nrow = m_meg_forward->nrow + m_eeg_forward->nrow; - sol->row_names.append(m_meg_forward->row_names); - } else if (iNMeg > 0) { - sol = m_meg_forward; - } else { - sol = m_eeg_forward; - } - - if(m_pSettings->compute_grad) { - if(iNMeg > 0 && iNEeg > 0) { - if(m_meg_forward_grad->data.cols() != m_eeg_forward_grad->data.cols()) { - qWarning() << "The MEG and EEG forward solutions do not match"; - return; - } - sol_grad->clear(); - sol_grad->data = MatrixXd(m_meg_forward_grad->nrow + m_eeg_forward_grad->nrow, m_meg_forward_grad->ncol); - sol_grad->data.block(0,0,m_meg_forward_grad->nrow,m_meg_forward_grad->ncol) = m_meg_forward_grad->data; - sol_grad->data.block(m_meg_forward_grad->nrow,0,m_eeg_forward_grad->nrow,m_eeg_forward_grad->ncol) = m_eeg_forward_grad->data; - sol_grad->nrow = m_meg_forward_grad->nrow + m_eeg_forward_grad->nrow; - sol_grad->row_names.append(m_meg_forward_grad->row_names); - } else if (iNMeg > 0) { - sol_grad = m_meg_forward_grad; - } else { - sol_grad = m_eeg_forward_grad; - } - } -} - -//========================================================================================================= - -void ComputeFwd::updateHeadPos(const FiffCoordTrans& transDevHead) -{ - - int iNMeg = 0; - if(m_megcoils) { - iNMeg = m_megcoils->ncoil; - } - - int iNComp = 0; - if(m_compcoils) { - iNComp = m_compcoils->ncoil; - } - - // create new coilset with updated head position - if (m_pSettings->coord_frame == FIFFV_COORD_MRI) { - FiffCoordTrans head_mri_t = m_mri_head_t.inverted(); - FiffCoordTrans meg_mri_t = FiffCoordTrans::combine(FIFFV_COORD_DEVICE,FIFFV_COORD_MRI,transDevHead,head_mri_t); - if (meg_mri_t.isEmpty()) { - return; - } - if ((m_megcoils = m_templates->create_meg_coils(m_listMegChs, - iNMeg, - m_pSettings->accurate ? FWD_COIL_ACCURACY_ACCURATE : FWD_COIL_ACCURACY_NORMAL, - meg_mri_t)) == Q_NULLPTR) { - return; - } - if (iNComp > 0) { - if ((m_compcoils = m_templates->create_meg_coils(m_listCompChs, - iNComp, - FWD_COIL_ACCURACY_NORMAL, - meg_mri_t)) == Q_NULLPTR) { - return; - } - } - } else { - if ((m_megcoils = m_templates->create_meg_coils(m_listMegChs, - iNMeg, - m_pSettings->accurate ? FWD_COIL_ACCURACY_ACCURATE : FWD_COIL_ACCURACY_NORMAL, - transDevHead)) == Q_NULLPTR) { - return; - } - - if (iNComp > 0) { - if ((m_compcoils = m_templates->create_meg_coils(m_listCompChs, - iNComp, - FWD_COIL_ACCURACY_NORMAL,transDevHead)) == Q_NULLPTR) { - return; - } - } - } - - // check if source spaces are still in head space - if(m_spaces[0]->coord_frame != FIFFV_COORD_HEAD) { - if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame,m_mri_head_t,m_spaces) != OK) { - return; - } - } - - // recompute meg forward - if ((FwdBemModel::compute_forward_meg(m_spaces, - m_megcoils, - m_compcoils, - m_compData.get(), // we might have to update this too - m_pSettings->fixed_ori, - m_bemModel, - &m_pSettings->r0, - m_pSettings->use_threads, - *m_meg_forward.data(), - *m_meg_forward_grad.data(), - m_pSettings->compute_grad)) == FAIL) { - return; - } - - // Update new Transformation Matrix - m_meg_head_t = transDevHead; - // update solution - sol->data.block(0,0,m_meg_forward->nrow,m_meg_forward->ncol) = m_meg_forward->data; - if(m_pSettings->compute_grad) { - sol_grad->data.block(0,0,m_meg_forward_grad->nrow,m_meg_forward_grad->ncol) = m_meg_forward_grad->data; - } -} - -//========================================================================================================= - -void ComputeFwd::storeFwd(const QString& sSolName) -{ - // We are ready to spill it out - // Transform the source spaces back into MRI coordinates - { - if (MNESourceSpace::transform_source_spaces_to(FIFFV_COORD_MRI,m_mri_head_t,m_spaces) != OK) { - return; - } - } - - int iNMeg = m_megcoils->ncoil; - int iNEeg = m_eegels->ncoil; - - QString sName; - - if(sSolName == "default") { - sName = m_pSettings->solname; - } else { - sName = sSolName; - } - - printf("\nwriting %s...",sSolName.toUtf8().constData()); - - if (!write_solution(sName, /* Destination file */ - m_spaces, /* The source spaces */ - m_pSettings->mriname,m_mri_id, /* MRI file and data obtained from there */ - m_mri_head_t, - m_pSettings->measname,m_meas_id, /* MEG file and data obtained from there */ - m_meg_head_t, - m_listMegChs, - iNMeg, - m_listEegChs, - iNEeg, - m_pSettings->fixed_ori, /* Fixed orientation dipoles? */ - m_pSettings->coord_frame, /* Coordinate frame */ - *m_meg_forward.data(), - *m_eeg_forward.data(), - *m_meg_forward_grad.data(), - *m_eeg_forward_grad.data(), - m_pSettings->compute_grad)) { - return; - } - - if (!mne_attach_env(m_pSettings->solname,m_pSettings->command)) { - return; - } - printf("done\n"); - printf("\nFinished.\n"); -} diff --git a/src/libraries/fwd/computeFwd/compute_fwd_settings.cpp b/src/libraries/fwd/computeFwd/compute_fwd_settings.cpp deleted file mode 100644 index f5e240bba70..00000000000 --- a/src/libraries/fwd/computeFwd/compute_fwd_settings.cpp +++ /dev/null @@ -1,375 +0,0 @@ - - -#include "compute_fwd_settings.h" - -#include - -using namespace Eigen; -using namespace FIFFLIB; -using namespace FWDLIB; - -#define X 0 -#define Y 1 -#define Z 2 - -#ifndef PROGRAM_VERSION -#define PROGRAM_VERSION "2.10" -#endif - -//============================================================================================================= -// STATIC DEFINITIONS -//============================================================================================================= - -//============================================================================================================= -// DEFINE MEMBER METHODS -//============================================================================================================= - -ComputeFwdSettings::ComputeFwdSettings() -{ - initMembers(); -} - -//============================================================================================================= - -ComputeFwdSettings::ComputeFwdSettings(int *argc,char **argv) -{ - initMembers(); - - if (!check_args(argc,argv)) - return; - -// mne_print_version_info(stderr,argv[0],PROGRAM_VERSION,__DATE__,__TIME__); - printf("%s version %s compiled at %s %s\n",argv[0],PROGRAM_VERSION,__DATE__,__TIME__); - - checkIntegrity(); -} - -//============================================================================================================= - -ComputeFwdSettings::~ComputeFwdSettings() -{ - //ToDo Garbage collection -} - -//============================================================================================================= - -void ComputeFwdSettings::checkIntegrity() -{ - if (srcname.isEmpty()) { - qCritical("Source space name is missing. Use the --src option to specify it."); - return; - } - if (!mri_head_ident) { - if (mriname.isEmpty() && transname.isEmpty()) { - qCritical("MRI <-> head coordinate transformation is missing. Use the --mri or --trans option to specify it."); - return; - } - } - if (measname.isEmpty()) { - qCritical("Source of coil and electrode locations is missing. Use the --meas option to specify it."); - return; - } - if (solname.isEmpty()) { - qCritical("Solution name is missing. Use the --fwd option to specify it."); - return; - } - if (! (include_meg || include_eeg)) { - qCritical("Employ the --meg and --eeg options to select MEG and/or EEG"); - return; - } -} - -//============================================================================================================= - -void ComputeFwdSettings::initMembers() -{ - // Init origin - r0 << 0.0f,0.0f,0.04f; - - filter_spaces = true; - accurate = false; - fixed_ori = false; - include_meg = false; - include_eeg = false; - compute_grad = false; - mindist = 0.0f; - coord_frame = FIFFV_COORD_HEAD; - do_all = false; - nlabel = 0; - - eeg_sphere_rad = 0.09f; - scale_eeg_pos = false; - use_equiv_eeg = true; - use_threads = true; - - pFiffInfo = Q_NULLPTR; - meg_head_t = FiffCoordTrans(); -} - -//============================================================================================================= - -void ComputeFwdSettings::usage(char *name) -{ - printf("usage : %s [options]\n",name); - printf("\t--meg to compute the MEG forward solution\n"); - printf("\t--eeg to compute the EEG forward solution\n"); - printf("\t--grad compute the gradient of the field with respect to the dipole coordinates as well\n"); - printf("\t--fixed to calculate only for the source orientation given by the surface normals\n"); - printf("\t--mricoord do calculations in MRI coordinates instead of head coordinates\n"); - printf("\t--accurate use more accurate coil definitions in MEG forward computation\n"); - printf("\t--src name specify the source space\n"); - printf("\t--label name label file to select the sources (can have multiple of these)\n"); - printf("\t--mri name take head/MRI coordinate transform from here (Neuromag MRI description file)\n"); - printf("\t--trans name take head/MRI coordinate transform from here (text file)\n"); - printf("\t--notrans head and MRI coordinate systems are identical.\n"); - printf("\t--meas name take MEG sensor and EEG electrode locations from here\n"); - printf("\t--bem name BEM model name\n"); - printf("\t--origin x:y:z/mm use a sphere model with this origin (head coordinates/mm)\n"); - printf("\t--eegscalp scale the electrode locations to the surface of the scalp when using a sphere model\n"); - printf("\t--eegmodels name read EEG sphere model specifications from here.\n"); - printf("\t--eegmodel name name of the EEG sphere model to use (default : Default)\n"); - printf("\t--eegrad rad/mm radius of the scalp surface to use in EEG sphere model (default : %7.1f mm)\n",1000*eeg_sphere_rad); - printf("\t--mindist dist/mm minimum allowable distance of the sources from the inner skull surface.\n"); - printf("\t--mindistout name Output the omitted source space points here.\n"); - printf("\t--includeall Omit all source space checks\n"); - printf("\t--all calculate forward solution in all nodes instead the selected ones only.\n"); - printf("\t--fwd name save the solution here\n"); - printf("\t--help print this info.\n"); - printf("\t--version print version info.\n\n"); - exit(1); -} - -//============================================================================================================= - -QString ComputeFwdSettings::build_command_line(QString old, QString new_item) -{ - if (!new_item.isEmpty() && new_item.size() > 0) { - if (!old.isEmpty()) { - old += " "; - } - old += new_item; - } - return old; -} - -//============================================================================================================= - -bool ComputeFwdSettings::check_unrecognized_args(int argc, char **argv) -{ - int k; - - if (argc > 1) { - printf("Unrecognized arguments : "); - for (k = 1; k < argc; k++) - printf("%s ",argv[k]); - printf("\n"); - qCritical("Check the command line."); - return false; - } - return true; -} - -//============================================================================================================= - -bool ComputeFwdSettings::check_args (int *argc,char **argv) -{ - int found; - char *last; - - if ((last = strrchr(argv[0],'/')) == NULL) - last = argv[0]; - else - last++; - command = build_command_line(command,last); - for (int k = 0; k < *argc; k++) { - found = 0; - if (strcmp(argv[k],"--version") == 0) { - printf("%s version %s compiled at %s %s\n", argv[0],PROGRAM_VERSION,__DATE__,__TIME__); - exit(0); - } - else if (strcmp(argv[k],"--help") == 0) { - usage(argv[0]); - exit(1); - } - else if (strcmp(argv[k],"--meg") == 0) { - found = 1; - include_meg = true; - } - else if (strcmp(argv[k],"--eeg") == 0) { - found = 1; - include_eeg = true; - } - else if (strcmp(argv[k],"--grad") == 0) { - found = 1; - compute_grad = true; - } - else if (strcmp(argv[k],"--all") == 0) { - found = 1; - do_all = true; - } - else if (strcmp(argv[k],"--accurate") == 0) { - found = 1; - accurate = true; - } - else if (strcmp(argv[k],"--fixed") == 0) { - found = 1; - fixed_ori = true; - } - else if (strcmp(argv[k],"--src") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--src: argument required."); - return false; - } - srcname = QString(argv[k+1]); - } - else if (strcmp(argv[k],"--mri") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--mri: argument required."); - return false; - } - mri_head_ident = false; - mriname = QString(argv[k+1]); - transname.clear(); - } - else if (strcmp(argv[k],"--trans") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--trans: argument required."); - return false; - } - mri_head_ident = false; - transname = QString(argv[k+1]); - mriname.clear(); - } - else if (strcmp(argv[k],"--notrans") == 0) { - found = 1; - mri_head_ident = true; - mriname.clear(); - transname.clear(); - } - else if (strcmp(argv[k],"--meas") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--meas: argument required."); - return false; - } - measname = QString(argv[k+1]); - } - else if (strcmp(argv[k],"--bem") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--bem: argument required."); - return false; - } - bemname = QString(argv[k+1]); - } - else if (strcmp(argv[k],"--origin") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--origin: argument required."); - return false; - } - if (sscanf(argv[k+1],"%f:%f:%f",r0[X],r0[Y],r0[Z]) != 3) { - qCritical("Could not interpret the origin."); - return false; - } - r0[X] = r0[X]/1000.0f; - r0[Y] = r0[Y]/1000.0f; - r0[Z] = r0[Z]/1000.0f; - } - else if (strcmp(argv[k],"--eegrad") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--eegrad: argument required."); - return false; - } - if (sscanf(argv[k+1],"%g",&eeg_sphere_rad) != 1) { - qCritical("Incomprehensible radius : %s",argv[k+1]); - return false; - } - if (eeg_sphere_rad <= 0) { - qCritical("Radius must be positive"); - return false; - } - eeg_sphere_rad = eeg_sphere_rad/1000.0f; - } - else if (strcmp(argv[k],"--eegmodels") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--eegmodels: argument required."); - return false; - } - eeg_model_file = QString(argv[k+1]); - } - else if (strcmp(argv[k],"--eegmodel") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--eegmodel: argument required."); - return false; - } - eeg_model_name = QString(argv[k+1]); - } - else if (strcmp(argv[k],"--eegscalp") == 0) { - found = 1; - scale_eeg_pos = true; - } - else if (strcmp(argv[k],"--mindist") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--mindist: argument required."); - return false; - } - if (sscanf(argv[k+1],"%f",&mindist) != 1) { - qCritical("Could not interpret the distance."); - return false; - } - if (mindist <= 0.0) - mindist = 0.0f; - mindist = mindist/1000.0f; - } - else if (strcmp(argv[k],"--includeall") == 0) { - found = 1; - filter_spaces = false; - } - else if (strcmp(argv[k],"--mindistout") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--mindistout: argument required."); - return false; - } - mindistoutname = QString(argv[k+1]); - } - else if (strcmp(argv[k],"--mricoord") == 0) { - found = 1; - coord_frame = FIFFV_COORD_MRI; - } - else if (strcmp(argv[k],"--fwd") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--fwd: argument required."); - return false; - } - solname = QString(argv[k+1]); - } - else if (strcmp(argv[k],"--label") == 0) { - found = 2; - if (k == *argc - 1) { - qCritical("--label: argument required."); - return false; - } - labels.append(QString(argv[k+1])); - nlabel++; - } - if (found) { - for (int p = k; p < k + found; p++) - command = build_command_line(command,argv[p]); - for (int p = k; p < *argc-found; p++) - argv[p] = argv[p+found]; - *argc = *argc - found; - k = k - found; - } - } - return check_unrecognized_args(*argc,argv); -} diff --git a/src/libraries/fwd/compute_fwd/compute_fwd.cpp b/src/libraries/fwd/compute_fwd/compute_fwd.cpp new file mode 100644 index 00000000000..cbc592ad722 --- /dev/null +++ b/src/libraries/fwd/compute_fwd/compute_fwd.cpp @@ -0,0 +1,727 @@ +//============================================================================================================= +/** + * @file compute_fwd.cpp + * @author Lorenz Esch ; + * Matti Hamalainen ; + * Ruben Dörfel ; + * Christoph Dinh + * @since 0.1.0 + * @date February, 2017 + * + * @section LICENSE + * + * Copyright (C) 2017, Lorenz Esch, Matti Hamalainen, Christoph Dinh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided with the distribution. + * * Neither the name of MNE-CPP authors nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + * @brief ComputeFwd class definition. + * + */ + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include "compute_fwd.h" +#include "../fwd_forward_solution.h" + +#include +#include + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= + +#include +#include +#include + +//============================================================================================================= +// USED NAMESPACES +//============================================================================================================= + +using namespace FWDLIB; +using namespace MNELIB; +using namespace FIFFLIB; +using namespace Eigen; + +//============================================================================================================= +// CONSTANTS +//============================================================================================================= + +constexpr int FAIL = -1; +constexpr int OK = 0; + +constexpr int X = 0; +constexpr int Y = 1; +constexpr int Z = 2; + +//============================================================================================================= +// DEFINE MEMBER METHODS +//============================================================================================================= + +ComputeFwd::ComputeFwd(ComputeFwdSettings::SPtr pSettings) + : m_meg_forward(new FiffNamedMatrix) + , m_meg_forward_grad(new FiffNamedMatrix) + , m_eeg_forward(new FiffNamedMatrix) + , m_eeg_forward_grad(new FiffNamedMatrix) + , m_pSettings(pSettings) +{ + initFwd(); +} + +//============================================================================================================= + +ComputeFwd::~ComputeFwd() +{ +} + +//============================================================================================================= + +//============================================================================================================= + +void ComputeFwd::initFwd() +{ + m_spaces.clear(); + m_iNSource = 0; + + m_mri_head_t = FiffCoordTrans(); + m_meg_head_t = FiffCoordTrans(); + + m_listMegChs = QList(); + m_listEegChs = QList(); + m_listCompChs = QList(); + + int iNMeg = 0; + int iNEeg = 0; + int iNComp = 0; + + m_templates.reset(); + m_megcoils.reset(); + m_compcoils.reset(); + m_eegels.reset(); + m_eegModels.reset(); + m_iNChan = 0; + + int k; + m_mri_id = FiffId(); + m_meas_id.clear(); + + QFile filteredFile; + QTextStream *filteredStream = Q_NULLPTR; + + m_eegModel.reset(); + m_bemModel.reset(); + + // Report the setup + printf("\n"); + printf("Source space : %s\n",m_pSettings->srcname.toUtf8().constData()); + if (!(m_pSettings->transname.isEmpty()) || !(m_pSettings->mriname.isEmpty())) { + printf("MRI -> head transform source : %s\n",!(m_pSettings->mriname.isEmpty()) ? m_pSettings->mriname.toUtf8().constData() : m_pSettings->transname.toUtf8().constData()); + } else { + printf("MRI and head coordinates are assumed to be identical.\n"); + } + printf("Measurement data : %s\n",m_pSettings->measname.toUtf8().constData()); + if (!m_pSettings->bemname.isEmpty()) { + printf("BEM model : %s\n",m_pSettings->bemname.toUtf8().constData()); + } else { + printf("Sphere model : origin at (% 7.2f % 7.2f % 7.2f) mm\n", + 1000.0f*m_pSettings->r0[X],1000.0f*m_pSettings->r0[Y],1000.0f*m_pSettings->r0[Z]); + if (m_pSettings->include_eeg) { + printf("\n"); + + if (m_pSettings->eeg_model_file.isEmpty()) { + qCritical("!!!!!!!!!!TODO: default_eeg_model_file();"); + } + m_eegModels.reset(FwdEegSphereModelSet::fwd_load_eeg_sphere_models(m_pSettings->eeg_model_file,m_eegModels.release())); + m_eegModels->fwd_list_eeg_sphere_models(stderr); + + if (m_pSettings->eeg_model_name.isEmpty()) { + m_pSettings->eeg_model_name = QString("Default"); + } + m_eegModel.reset(m_eegModels->fwd_select_eeg_sphere_model(m_pSettings->eeg_model_name)); + if (!m_eegModel) { + return; + } + + if (!m_eegModel->fwd_setup_eeg_sphere_model(m_pSettings->eeg_sphere_rad,m_pSettings->use_equiv_eeg,3)) { + return; + } + + printf("Using EEG sphere model \"%s\" with scalp radius %7.1f mm\n", + m_pSettings->eeg_model_name.toUtf8().constData(),1000*m_pSettings->eeg_sphere_rad); + printf("%s the electrode locations to scalp\n",m_pSettings->scale_eeg_pos ? "Scale" : "Do not scale"); + + m_eegModel->scale_pos = m_pSettings->scale_eeg_pos; + m_eegModel->r0 = m_pSettings->r0; + + printf("\n"); + } + } + printf("%s field computations\n",m_pSettings->accurate ? "Accurate" : "Standard"); + printf("Do computations in %s coordinates.\n",FiffCoordTrans::frame_name(m_pSettings->coord_frame).toUtf8().constData()); + printf("%s source orientations\n",m_pSettings->fixed_ori ? "Fixed" : "Free"); + if (m_pSettings->compute_grad) { + printf("Compute derivatives with respect to source location coordinates\n"); + } + printf("Destination for the solution : %s\n",m_pSettings->solname.toUtf8().constData()); + if (m_pSettings->do_all) { + printf("Calculate solution for all source locations.\n"); + } + if (m_pSettings->nlabel > 0) { + printf("Source space will be restricted to sources in %d labels\n",m_pSettings->nlabel); + } + + // Read the source locations + + printf("\n"); + printf("Reading %s...\n",m_pSettings->srcname.toUtf8().constData()); + if (MNESourceSpace::read_source_spaces(m_pSettings->srcname,m_spaces) != OK) { + return; + } + for (k = 0, m_iNSource = 0; k < static_cast(m_spaces.size()); k++) { + if (m_pSettings->do_all) { + m_spaces[k]->enable_all_sources(); + } + m_iNSource += m_spaces[k]->nuse; + } + if (m_iNSource == 0) { + qCritical("No sources are active in these source spaces. --all option should be used."); + return; + } + printf("Read %d source spaces a total of %d active source locations\n", static_cast(m_spaces.size()),m_iNSource); + if (MNESourceSpace::restrict_sources_to_labels(m_spaces,m_pSettings->labels,m_pSettings->nlabel) == FAIL) { + return; + } + + // Read the MRI -> head coordinate transformation + printf("\n"); + if (!m_pSettings->mriname.isEmpty()) { + m_mri_head_t = FiffCoordTrans::readMriTransform(m_pSettings->mriname); + if (m_mri_head_t.isEmpty()) { + return; + } + { + QFile mriFile(m_pSettings->mriname); + FiffStream::SPtr mriStream(new FiffStream(&mriFile)); + if (mriStream->open()) { + m_mri_id.version = mriStream->id().version; + m_mri_id.machid[0] = mriStream->id().machid[0]; + m_mri_id.machid[1] = mriStream->id().machid[1]; + m_mri_id.time = mriStream->id().time; + mriStream->close(); + } else { + mriStream->close(); + m_mri_id = FiffId(); + } + } + if (m_mri_id.isEmpty()) { + qCritical("Couldn't read MRI file id (How come?)"); + return; + } + } + else if (!m_pSettings->transname.isEmpty()) { + FiffCoordTrans t = FiffCoordTrans::readFShead2mriTransform(m_pSettings->transname.toUtf8().data()); + if (t.isEmpty()) { + return; + } + m_mri_head_t = t.inverted(); + } else { + m_mri_head_t = FiffCoordTrans::identity(FIFFV_COORD_MRI,FIFFV_COORD_HEAD); + } + m_mri_head_t.print(); + + // Read the channel information and the MEG device -> head coordinate transformation + + if(!m_pSettings->pFiffInfo) { + + QFile measname(m_pSettings->measname); + FIFFLIB::FiffDirNode::SPtr DirNode; + FiffStream::SPtr pStream(new FiffStream(&measname)); + FIFFLIB::FiffInfo fiffInfo; + if(!pStream->open()) { + qCritical() << "Could not open Stream."; + return; + } + + if(!pStream->read_meas_info(pStream->dirtree(), fiffInfo, DirNode)){ + qCritical() << "Could not find the channel information."; + return; + } + pStream->close(); + m_pInfoBase = QSharedPointer(new FiffInfo(fiffInfo)); + } else { + m_pInfoBase = m_pSettings->pFiffInfo; + } + if(!m_pInfoBase) { + qCritical ("ComputeFwd::initFwd(): no FiffInfo"); + return; + } + m_pInfoBase->mne_read_meg_comp_eeg_ch_info(m_listMegChs, + iNMeg, + m_listCompChs, + iNComp, + m_listEegChs, + iNEeg, + m_meg_head_t, + m_meas_id); + if (!m_pSettings->meg_head_t.isEmpty()) { + m_meg_head_t = m_pSettings->meg_head_t; + } + if (m_meg_head_t.isEmpty()) { + qCritical("MEG -> head coordinate transformation not found."); + return; + } + + m_iNChan = iNMeg + iNEeg; + + printf("\n"); + if (iNMeg > 0) { + printf("Read %3d MEG channels from %s\n",iNMeg,m_pSettings->measname.toUtf8().constData()); + } + if (iNComp > 0) { + printf("Read %3d MEG compensation channels from %s\n",iNComp,m_pSettings->measname.toUtf8().constData()); + } + if (iNEeg > 0) { + printf("Read %3d EEG channels from %s\n",iNEeg,m_pSettings->measname.toUtf8().constData()); + } + if (!m_pSettings->include_meg) { + printf("MEG not requested. MEG channels omitted.\n"); + m_listMegChs.clear(); + m_listCompChs.clear(); + iNMeg = 0; + iNComp = 0; + } + else + m_meg_head_t.print(); + if (!m_pSettings->include_eeg) { + printf("EEG not requested. EEG channels omitted.\n"); + m_listEegChs.clear(); + iNEeg = 0; + } else { + if (!FiffChInfo::checkEegLocations(m_listEegChs, iNEeg)) { + return; + } + } + + // Create coil descriptions with transformation to head or MRI frame + + if (m_pSettings->include_meg) { + m_qPath = QString(QCoreApplication::applicationDirPath() + "/../resources/general/coilDefinitions/coil_def.dat"); + if ( !QCoreApplication::startingUp() ) { + m_qPath = QCoreApplication::applicationDirPath() + QString("/../resources/general/coilDefinitions/coil_def.dat"); + } else if (!QFile::exists(m_qPath)) { + m_qPath = "../resources/general/coilDefinitions/coil_def.dat"; + } + + m_templates.reset(FwdCoilSet::read_coil_defs(m_qPath)); + if (!m_templates) { + return; + } + + // Compensation data + + m_compData = MNECTFCompDataSet::read(m_pSettings->measname); + if (!m_compData) { + return; + } + if (m_compData->ncomp > 0) { + printf("%d compensation data sets in %s\n",m_compData->ncomp,m_pSettings->measname.toUtf8().constData()); + } else { + m_listCompChs.clear(); + iNComp = 0; + + m_compData.reset(); + } + } + if (m_pSettings->coord_frame == FIFFV_COORD_MRI) { + FiffCoordTrans head_mri_t = m_mri_head_t.inverted(); + FiffCoordTrans meg_mri_t = FiffCoordTrans::combine(FIFFV_COORD_DEVICE,FIFFV_COORD_MRI,m_meg_head_t,head_mri_t); + if (meg_mri_t.isEmpty()) { + return; + } + m_megcoils.reset(m_templates->create_meg_coils(m_listMegChs, + iNMeg, + m_pSettings->accurate ? FWD_COIL_ACCURACY_ACCURATE : FWD_COIL_ACCURACY_NORMAL, + meg_mri_t)); + if (!m_megcoils) { + return; + } + if (iNComp > 0) { + m_compcoils.reset(m_templates->create_meg_coils(m_listCompChs, + iNComp, + FWD_COIL_ACCURACY_NORMAL, + meg_mri_t)); + if (!m_compcoils) { + return; + } + } + m_eegels.reset(FwdCoilSet::create_eeg_els(m_listEegChs, + iNEeg, + head_mri_t)); + if (!m_eegels) { + return; + } + + printf("MRI coordinate coil definitions created.\n"); + } else { + m_megcoils.reset(m_templates->create_meg_coils(m_listMegChs, + iNMeg, + m_pSettings->accurate ? FWD_COIL_ACCURACY_ACCURATE : FWD_COIL_ACCURACY_NORMAL, + m_meg_head_t)); + if (!m_megcoils) { + return; + } + + if (iNComp > 0) { + m_compcoils.reset(m_templates->create_meg_coils(m_listCompChs, + iNComp, + FWD_COIL_ACCURACY_NORMAL,m_meg_head_t)); + if (!m_compcoils) { + return; + } + } + m_eegels.reset(FwdCoilSet::create_eeg_els(m_listEegChs, + iNEeg)); + if (!m_eegels) { + return; + } + printf("Head coordinate coil definitions created.\n"); + } + + // Transform the source spaces into the appropriate coordinates + { + if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame,m_mri_head_t,m_spaces) != OK) { + return; + } + } + printf("Source spaces are now in %s coordinates.\n",FiffCoordTrans::frame_name(m_pSettings->coord_frame).toUtf8().constData()); + + // Prepare the BEM model if necessary + + if (!m_pSettings->bemname.isEmpty()) { + QString bemsolname = FwdBemModel::fwd_bem_make_bem_sol_name(m_pSettings->bemname); + m_pSettings->bemname = bemsolname; + + printf("\nSetting up the BEM model using %s...\n",m_pSettings->bemname.toUtf8().constData()); + printf("\nLoading surfaces...\n"); + m_bemModel = FwdBemModel::fwd_bem_load_three_layer_surfaces(m_pSettings->bemname); + + if (m_bemModel) { + printf("Three-layer model surfaces loaded.\n"); + } + else { + m_bemModel = FwdBemModel::fwd_bem_load_homog_surface(m_pSettings->bemname); + if (!m_bemModel) { + return; + } + printf("Homogeneous model surface loaded.\n"); + } + if (iNEeg > 0 && m_bemModel->nsurf == 1) { + qCritical("Cannot use a homogeneous model in EEG calculations."); + return; + } + printf("\nLoading the solution matrix...\n"); + if (m_bemModel->fwd_bem_load_recompute_solution(m_pSettings->bemname.toUtf8().data(),FWD_BEM_UNKNOWN,false) == FAIL) { + return; + } + if (m_pSettings->coord_frame == FIFFV_COORD_HEAD) { + printf("Employing the head->MRI coordinate transform with the BEM model.\n"); + if (m_bemModel->fwd_bem_set_head_mri_t(m_mri_head_t) == FAIL) { + return; + } + } + printf("BEM model %s is now set up\n",m_bemModel->sol_name.toUtf8().constData()); + } else { + printf("Using the sphere model.\n"); + } + printf ("\n"); + + // Try to circumvent numerical problems by excluding points too close or outside the inner skull surface + + if (m_pSettings->filter_spaces) { + if (!m_pSettings->mindistoutname.isEmpty()) { + filteredFile.setFileName(m_pSettings->mindistoutname); + if (!filteredFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + qCritical() << m_pSettings->mindistoutname; + return; + } + filteredStream = new QTextStream(&filteredFile); + printf("Omitted source space points will be output to : %s\n",m_pSettings->mindistoutname.toUtf8().constData()); + } + MNESourceSpace::filter_source_spaces(m_pSettings->mindist, + m_pSettings->bemname, + m_mri_head_t, + m_spaces, + filteredStream,m_pSettings->use_threads); + delete filteredStream; + filteredStream = Q_NULLPTR; + } +} + +//============================================================================================================= + +void ComputeFwd::populateMetadata(FwdForwardSolution& fwd) +{ + fwd.coord_frame = m_pSettings->coord_frame; + fwd.source_ori = m_pSettings->fixed_ori ? FIFFV_MNE_FIXED_ORI : FIFFV_MNE_FREE_ORI; + fwd.surf_ori = false; + fwd.mri_filename = m_pSettings->mriname; + + int nmeg = m_megcoils ? m_megcoils->ncoil() : 0; + int neeg = m_eegels ? m_eegels->ncoil() : 0; + fwd.nchan = nmeg + neeg; + + fwd.mri_head_t = m_mri_head_t; + fwd.mri_id = m_mri_id; + + // Source spaces: store in MRI frame (FIFF convention), keep m_spaces in computation frame + { + if (MNESourceSpace::transform_source_spaces_to(FIFFV_COORD_MRI, m_mri_head_t, m_spaces) != OK) { + return; + } + fwd.src.clear(); + fwd.nsource = 0; + for (int i = 0; i < static_cast(m_spaces.size()); ++i) { + fwd.src.append(*m_spaces[i]); + fwd.nsource += m_spaces[i]->nuse; + } + if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame, m_mri_head_t, m_spaces) != OK) { + return; + } + } + + // Measurement provenance + fwd.info.filename = m_pSettings->measname; + fwd.info.meas_id = m_meas_id; + fwd.info.dev_head_t = m_meg_head_t; + fwd.info.nchan = fwd.nchan; + + fwd.info.chs.clear(); + fwd.info.ch_names.clear(); + for (int i = 0; i < nmeg; ++i) { + fwd.info.chs.append(m_listMegChs[i]); + fwd.info.ch_names.append(m_listMegChs[i].ch_name); + } + for (int i = 0; i < neeg; ++i) { + fwd.info.chs.append(m_listEegChs[i]); + fwd.info.ch_names.append(m_listEegChs[i].ch_name); + } + + // Bad channels + if (!m_pSettings->measname.isEmpty()) { + QFile fileBad(m_pSettings->measname); + FiffStream::SPtr t_pStreamBads(new FiffStream(&fileBad)); + if (t_pStreamBads->open()) { + fwd.info.bads = t_pStreamBads->read_bad_channels(t_pStreamBads->dirtree()); + t_pStreamBads->close(); + } + } +} + +//============================================================================================================= + +std::unique_ptr ComputeFwd::calculateFwd() +{ + auto fwdSolution = std::make_unique(); + populateMetadata(*fwdSolution); + int iNMeg = 0; + int iNEeg = 0; + + if(m_megcoils) { + iNMeg = m_megcoils->ncoil(); + } + if(m_eegels) { + iNEeg = m_eegels->ncoil(); + } + if (!m_bemModel) { + m_pSettings->use_threads = false; + } + + // check if source spaces are still in computation frame + if(m_spaces[0]->coord_frame != m_pSettings->coord_frame) { + if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame,m_mri_head_t,m_spaces) != OK) { + return nullptr; + } + } + + // Do the actual computation + if (iNMeg > 0) { + if ((m_bemModel->compute_forward_meg(m_spaces, + m_megcoils.get(), + m_compcoils.get(), + m_compData.get(), + m_pSettings->fixed_ori, + m_pSettings->r0, + m_pSettings->use_threads, + *m_meg_forward.data(), + *m_meg_forward_grad.data(), + m_pSettings->compute_grad)) == FAIL) { + return nullptr; + } + } + if (iNEeg > 0) { + if ((m_bemModel->compute_forward_eeg(m_spaces, + m_eegels.get(), + m_pSettings->fixed_ori, + m_eegModel.get(), + m_pSettings->use_threads, + *m_eeg_forward.data(), + *m_eeg_forward_grad.data(), + m_pSettings->compute_grad))== FAIL) { + return nullptr; + } + } + + // Assemble combined sol + if(iNMeg > 0 && iNEeg > 0) { + if(m_meg_forward->data.cols() != m_eeg_forward->data.cols()) { + qWarning() << "The MEG and EEG forward solutions do not match"; + return nullptr; + } + fwdSolution->sol->clear(); + fwdSolution->sol->nrow = m_meg_forward->nrow + m_eeg_forward->nrow; + fwdSolution->sol->ncol = m_meg_forward->ncol; + fwdSolution->sol->data = MatrixXd(fwdSolution->sol->nrow, fwdSolution->sol->ncol); + fwdSolution->sol->data.block(0,0,m_meg_forward->nrow,m_meg_forward->ncol) = m_meg_forward->data; + fwdSolution->sol->data.block(m_meg_forward->nrow,0,m_eeg_forward->nrow,m_eeg_forward->ncol) = m_eeg_forward->data; + fwdSolution->sol->row_names = m_meg_forward->row_names; + fwdSolution->sol->row_names.append(m_eeg_forward->row_names); + fwdSolution->sol->col_names = m_meg_forward->col_names; + } else if (iNMeg > 0) { + fwdSolution->sol = m_meg_forward; + } else { + fwdSolution->sol = m_eeg_forward; + } + + if(m_pSettings->compute_grad) { + if(iNMeg > 0 && iNEeg > 0) { + if(m_meg_forward_grad->data.cols() != m_eeg_forward_grad->data.cols()) { + qWarning() << "The MEG and EEG forward solutions do not match"; + return nullptr; + } + fwdSolution->sol_grad->clear(); + fwdSolution->sol_grad->nrow = m_meg_forward_grad->nrow + m_eeg_forward_grad->nrow; + fwdSolution->sol_grad->ncol = m_meg_forward_grad->ncol; + fwdSolution->sol_grad->data = MatrixXd(fwdSolution->sol_grad->nrow, fwdSolution->sol_grad->ncol); + fwdSolution->sol_grad->data.block(0,0,m_meg_forward_grad->nrow,m_meg_forward_grad->ncol) = m_meg_forward_grad->data; + fwdSolution->sol_grad->data.block(m_meg_forward_grad->nrow,0,m_eeg_forward_grad->nrow,m_eeg_forward_grad->ncol) = m_eeg_forward_grad->data; + fwdSolution->sol_grad->row_names = m_meg_forward_grad->row_names; + fwdSolution->sol_grad->row_names.append(m_eeg_forward_grad->row_names); + fwdSolution->sol_grad->col_names = m_meg_forward_grad->col_names; + } else if (iNMeg > 0) { + fwdSolution->sol_grad = m_meg_forward_grad; + } else { + fwdSolution->sol_grad = m_eeg_forward_grad; + } + } + + return fwdSolution; +} + +//============================================================================================================= + +bool ComputeFwd::updateHeadPos(const FiffCoordTrans& transDevHead, FwdForwardSolution& fwd) +{ + int iNMeg = 0; + if(m_megcoils) { + iNMeg = m_megcoils->ncoil(); + } + + int iNComp = 0; + if(m_compcoils) { + iNComp = m_compcoils->ncoil(); + } + + // create new coilset with updated head position + if (m_pSettings->coord_frame == FIFFV_COORD_MRI) { + FiffCoordTrans head_mri_t = m_mri_head_t.inverted(); + FiffCoordTrans meg_mri_t = FiffCoordTrans::combine(FIFFV_COORD_DEVICE,FIFFV_COORD_MRI,transDevHead,head_mri_t); + if (meg_mri_t.isEmpty()) { + return false; + } + m_megcoils.reset(m_templates->create_meg_coils(m_listMegChs, + iNMeg, + m_pSettings->accurate ? FWD_COIL_ACCURACY_ACCURATE : FWD_COIL_ACCURACY_NORMAL, + meg_mri_t)); + if (!m_megcoils) { + return false; + } + if (iNComp > 0) { + m_compcoils.reset(m_templates->create_meg_coils(m_listCompChs, + iNComp, + FWD_COIL_ACCURACY_NORMAL, + meg_mri_t)); + if (!m_compcoils) { + return false; + } + } + } else { + m_megcoils.reset(m_templates->create_meg_coils(m_listMegChs, + iNMeg, + m_pSettings->accurate ? FWD_COIL_ACCURACY_ACCURATE : FWD_COIL_ACCURACY_NORMAL, + transDevHead)); + if (!m_megcoils) { + return false; + } + + if (iNComp > 0) { + m_compcoils.reset(m_templates->create_meg_coils(m_listCompChs, + iNComp, + FWD_COIL_ACCURACY_NORMAL,transDevHead)); + if (!m_compcoils) { + return false; + } + } + } + + // check if source spaces are still in computation frame + if(m_spaces[0]->coord_frame != m_pSettings->coord_frame) { + if (MNESourceSpace::transform_source_spaces_to(m_pSettings->coord_frame,m_mri_head_t,m_spaces) != OK) { + return false; + } + } + + // recompute meg forward + if ((m_bemModel->compute_forward_meg(m_spaces, + m_megcoils.get(), + m_compcoils.get(), + m_compData.get(), + m_pSettings->fixed_ori, + m_pSettings->r0, + m_pSettings->use_threads, + *m_meg_forward.data(), + *m_meg_forward_grad.data(), + m_pSettings->compute_grad)) == FAIL) { + return false; + } + + // Update transformation matrix and info + m_meg_head_t = transDevHead; + fwd.info.dev_head_t = transDevHead; + + // update solution + fwd.sol->data.block(0,0,m_meg_forward->nrow,m_meg_forward->ncol) = m_meg_forward->data; + if(m_pSettings->compute_grad) { + fwd.sol_grad->data.block(0,0,m_meg_forward_grad->nrow,m_meg_forward_grad->ncol) = m_meg_forward_grad->data; + } + + return true; +} diff --git a/src/libraries/fwd/computeFwd/compute_fwd.h b/src/libraries/fwd/compute_fwd/compute_fwd.h similarity index 53% rename from src/libraries/fwd/computeFwd/compute_fwd.h rename to src/libraries/fwd/compute_fwd/compute_fwd.h index 5cb94a0ce3c..82befb60150 100644 --- a/src/libraries/fwd/computeFwd/compute_fwd.h +++ b/src/libraries/fwd/compute_fwd/compute_fwd.h @@ -31,12 +31,12 @@ * POSSIBILITY OF SUCH DAMAGE. * * - * @brief Compute Forward Setting class declaration. + * @brief ComputeFwd class declaration. * */ -#ifndef COMPUTEFWD_H -#define COMPUTEFWD_H +#ifndef COMPUTE_FWD_H +#define COMPUTE_FWD_H //============================================================================================================= // INCLUDES @@ -46,45 +46,41 @@ #include "compute_fwd_settings.h" #include +#include +#include + #include "../fwd_coil_set.h" -#include #include "../fwd_eeg_sphere_model_set.h" #include "../fwd_bem_model.h" -#include -#include +#include #include -#include - -#include - -#include -#include - -#include //============================================================================================================= -// EIGEN INCLUDES +// STL INCLUDES //============================================================================================================= -#include +#include +#include //============================================================================================================= // QT INCLUDES //============================================================================================================= #include +#include #include -#include -#include -#include - //============================================================================================================= // FORWARD DECLARATIONS //============================================================================================================= -namespace MNELIB { - class MNEForwardSolution; + +namespace FIFFLIB { + class FiffNamedMatrix; +} + +namespace FWDLIB { + class FwdForwardSolution; } //============================================================================================================= @@ -96,9 +92,14 @@ namespace FWDLIB //============================================================================================================= /** - * Implements the compute forward solution + * Implements the forward solution computation. * - * @brief Compute Forward implementation + * ComputeFwd is a worker/factory that takes ComputeFwdSettings, initialises + * all required data structures (coil definitions, BEM model, source spaces, …) + * and performs the actual forward calculation. The computed + * FwdForwardSolution is returned by calculateFwd() and updateHeadPos(). + * + * @brief Forward solution computation worker. */ class FWDSHARED_EXPORT ComputeFwd { @@ -108,115 +109,94 @@ class FWDSHARED_EXPORT ComputeFwd //========================================================================================================= /** - * Default Constructor - * @param[in] p_settings The pointer that contains the setting information. + * Constructs a ComputeFwd and initialises all data structures needed for the + * forward computation according to @a pSettings. + * + * @param[in] pSettings Shared pointer to the forward computation settings. */ explicit ComputeFwd(ComputeFwdSettings::SPtr pSettings); //========================================================================================================= /** - * Destructs the Compute Forward solution class + * Destructor. */ virtual ~ComputeFwd(); //========================================================================================================= /** - * calculate Forward solution - */ - void calculateFwd(); - - //========================================================================================================= - /** - * Update the heaposition with meg_head_t and recalculate the forward solution for meg - * @param[in] transDevHeadOld The meg <-> head transformation to use for updating head position. + * Perform the forward calculation. + * + * @return The computed forward solution, or nullptr on error. */ - void updateHeadPos(const FIFFLIB::FiffCoordTrans& transDevHead); + std::unique_ptr calculateFwd(); //========================================================================================================= /** - * Store Forward solution with given name. It defaults the name specified in - * ComputeFwdSettings::settings->solname. - * @param[in] sSolName The file name to store the currnt forward solution. + * Update the head position with a new device-to-head transform and + * recompute the MEG portion of the forward solution. * + * @param[in] transDevHead The updated device-to-head coordinate transform. + * @param[in,out] fwd The forward solution to update in place. + * @return True on success, false on error. */ - void storeFwd(const QString& sSolName = "default"); - - // ToDo: make MNEForwardSolution the main output for the solution - // QSharedPointer fwdSolution; /**< MNE Forward solution that contains all results. */ - - QSharedDataPointer sol; /**< Forward solution (will be part of fwdSolution once rafactored). */ - QSharedDataPointer sol_grad; /**< Forward solution (Grad) (will be part of fwdSolution once rafactored). */ - - QSharedDataPointer m_meg_forward; /**< The MEG forward calculation. */ - QSharedDataPointer m_meg_forward_grad; /**< The MEG gradient forward calculation*/ - QSharedDataPointer m_eeg_forward; /**< The EEG forward calculation. */ - QSharedDataPointer m_eeg_forward_grad; /**< The EEG gradient forward calculation*/ - - QString qPath; - QFile file; + bool updateHeadPos(const FIFFLIB::FiffCoordTrans& transDevHead, FwdForwardSolution& fwd); private: //========================================================================================================= /** - * init the member variables + * Read source spaces, coordinate transforms, channel information, coil + * definitions, compensation data, and BEM model. Populates all + * computation state members and the public fields of fwdSolution. */ void initFwd(); - std::vector> m_spaces; /**< Source spaces. */ - int m_iNSource; /**< Number of source space points. */ - FwdCoilSet* m_templates; /**< The template coil set. */ - FwdCoilSet* m_megcoils; /**< The MEG coil set. */ - FwdCoilSet* m_compcoils; /**< The compensator coil set. */ - FwdCoilSet* m_eegels; /**< The EEG eceltrode set. */ - std::unique_ptr m_compData; /**< The compensator data. */ - FwdEegSphereModelSet* m_eegModels; /**< The EEG model set. */ - FwdEegSphereModel* m_eegModel; /**< The EEG model. */ - FwdBemModel *m_bemModel; /**< BEM model definition. */ - Eigen::Vector3f *m_r0; /**< The Sphere model origin. */ - - QList m_listMegChs; /**< The MEG channel information. */ - QList m_listEegChs; /**< The EEG channel information. */ - QList m_listCompChs; /**< The Compensator Channel List. */ - int m_iNChan; /**< The number of channels. */ - - FIFFLIB::fiffId m_mri_id; /**< The MRI ID. */ - FIFFLIB::FiffId m_meas_id; /**< The Measurement ID. */ - FIFFLIB::FiffCoordTrans m_mri_head_t; /**< The MRI->head coordinate transformation. */ - FIFFLIB::FiffCoordTrans m_meg_head_t; /**< The MEG->head coordinate transformation. */ - - QSharedPointer m_pInfoBase; - - ComputeFwdSettings::SPtr m_pSettings; /**< The settings for the forward calculation. */ - //========================================================================================================= /** - * Read channelinformation and split into lists for meg/eeg/comp + read m_meg_head_t - * @param[in] pFiffInfo The FiffInfo to read from. - * @param[in, out] listMegCh The MEG channel list. - * @param[in, out] iNMeg The number of MEG channels. - * @param[in, out] listMegComp The compensator channel list. - * @param[in, out] iNMegCmp The number of compensator channels. - * @param[in, out] listEegCh The EEG channel list. - * @param[in, out] iNEeg The number of EEG channels. - * @param[in, out] transDevHeadOld The meg <-> head transformation. - * @param[in, out] id The FiffID. - * + * Populate metadata fields of a forward solution from the current + * computation state (settings, source spaces, channel info, etc.). */ - int mne_read_meg_comp_eeg_ch_info_41(FIFFLIB::FiffInfoBase::SPtr pFiffInfoBase, - QList& listMegCh, - int& iNMeg, - QList& listMegComp, - int& iNMegCmp, - QList& listEegCh, - int& iNEeg, - FIFFLIB::FiffCoordTrans& transDevHead, - FIFFLIB::FiffId& id); + void populateMetadata(FwdForwardSolution& fwd); + + //========================================================================================================= + // Computation state + //========================================================================================================= + std::vector> m_spaces; /**< Source spaces. */ + int m_iNSource = 0; /**< Number of active source points. */ + std::unique_ptr m_templates; /**< Template coil set. */ + std::unique_ptr m_megcoils; /**< MEG coil set. */ + std::unique_ptr m_compcoils; /**< Compensator coil set. */ + std::unique_ptr m_eegels; /**< EEG electrode set. */ + std::unique_ptr m_compData; /**< CTF compensation data. */ + std::unique_ptr m_eegModels; /**< EEG sphere model set. */ + std::unique_ptr m_eegModel; /**< Active EEG sphere model. */ + std::unique_ptr m_bemModel; /**< BEM model. */ + + QList m_listMegChs; /**< MEG channel information. */ + QList m_listEegChs; /**< EEG channel information. */ + QList m_listCompChs; /**< Compensator channel list. */ + int m_iNChan = 0; /**< Number of channels. */ + + FIFFLIB::FiffId m_mri_id; /**< MRI file ID. */ + FIFFLIB::FiffId m_meas_id; /**< Measurement ID. */ + FIFFLIB::FiffCoordTrans m_mri_head_t; /**< MRI-to-head transform. */ + FIFFLIB::FiffCoordTrans m_meg_head_t; /**< MEG-to-head transform. */ + + QSharedPointer m_pInfoBase; /**< Measurement info. */ + ComputeFwdSettings::SPtr m_pSettings; /**< Forward computation settings. */ + + QSharedDataPointer m_meg_forward; /**< MEG forward matrix. */ + QSharedDataPointer m_meg_forward_grad; /**< MEG gradient forward matrix. */ + QSharedDataPointer m_eeg_forward; /**< EEG forward matrix. */ + QSharedDataPointer m_eeg_forward_grad; /**< EEG gradient forward matrix. */ + + QString m_qPath; /**< Coil definition file path. */ }; //============================================================================================================= // INLINE DEFINITIONS //============================================================================================================= -} //NAMESPACE -#endif // COMPUTEFWDSETTINGS_H +} // namespace FWDLIB + +#endif // COMPUTE_FWD_H diff --git a/src/libraries/inverse/IInverseAlgorithm.h b/src/libraries/fwd/compute_fwd/compute_fwd_settings.cpp similarity index 52% rename from src/libraries/inverse/IInverseAlgorithm.h rename to src/libraries/fwd/compute_fwd/compute_fwd_settings.cpp index 9f3bbe566aa..c70e616c90c 100644 --- a/src/libraries/inverse/IInverseAlgorithm.h +++ b/src/libraries/fwd/compute_fwd/compute_fwd_settings.cpp @@ -1,15 +1,16 @@ + //============================================================================================================= /** - * @file IInverseAlgorithm.h + * @file compute_fwd_settings.cpp * @author Lorenz Esch ; * Matti Hamalainen ; * Christoph Dinh * @since 0.1.0 - * @date February, 2013 + * @date February, 2017 * * @section LICENSE * - * Copyright (C) 2013, Lorenz Esch, Matti Hamalainen, Christoph Dinh. All rights reserved. + * Copyright (C) 2017, Lorenz Esch, Matti Hamalainen, Christoph Dinh. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that * the following conditions are met: @@ -30,103 +31,115 @@ * POSSIBILITY OF SUCH DAMAGE. * * - * @brief Contains declaration of IIinverseAlgorithm interface class. + * @brief ComputeFwdSettings class definition. * */ -#ifndef IINVERSEALGORITHM_H -#define IINVERSEALGORITHM_H - //============================================================================================================= // INCLUDES //============================================================================================================= -#include -#include +#include "compute_fwd_settings.h" + +//============================================================================================================= +// USED NAMESPACES +//============================================================================================================= + +using namespace Eigen; +using namespace FIFFLIB; +using namespace FWDLIB; + +//============================================================================================================= +// DEFINES +//============================================================================================================= + +//============================================================================================================= +// STATIC DEFINITIONS +//============================================================================================================= //============================================================================================================= -// FORWARD DECLARATIONS +// DEFINE MEMBER METHODS //============================================================================================================= -namespace FIFFLIB + +/** + * Constructs a ComputeFwdSettings object with default member values. + */ +ComputeFwdSettings::ComputeFwdSettings() { -class FiffEvoked; + initMembers(); } -namespace MNELIB +//============================================================================================================= + +/** + * Destroys the ComputeFwdSettings object. + */ +ComputeFwdSettings::~ComputeFwdSettings() { -class MNESourceSpaces; -class MNESourceEstimate; + //ToDo Garbage collection } -//============================================================================================================= -// DEFINE NAMESPACE INVERSELIB //============================================================================================================= -namespace INVERSELIB +/** + * Validate that all required settings have been specified. + * + * Checks for source space name, MRI-to-head transform, measurement file, + * solution output name, and that at least MEG or EEG is selected. + */ +void ComputeFwdSettings::checkIntegrity() { + if (srcname.isEmpty()) { + qCritical("Source space name is missing. Use the --src option to specify it."); + return; + } + if (!mri_head_ident) { + if (mriname.isEmpty() && transname.isEmpty()) { + qCritical("MRI <-> head coordinate transformation is missing. Use the --mri or --trans option to specify it."); + return; + } + } + if (measname.isEmpty()) { + qCritical("Source of coil and electrode locations is missing. Use the --meas option to specify it."); + return; + } + if (solname.isEmpty()) { + qCritical("Solution name is missing. Use the --fwd option to specify it."); + return; + } + if (! (include_meg || include_eeg)) { + qCritical("Employ the --meg and --eeg options to select MEG and/or EEG"); + return; + } +} -//============================================================================================================= -// INVERSE FORWARD DECLARATIONS //============================================================================================================= -//============================================================================================================= /** - * Inverse algorithm interface for inverse routines of MNE-CPP - * - * @brief Inverse algorithm interface + * Initialize all data members to their default values. */ -class IInverseAlgorithm +void ComputeFwdSettings::initMembers() { -public: - //========================================================================================================= - /** - * Destroys the IInverseAlgorithm. - */ - virtual ~IInverseAlgorithm() {} - - //========================================================================================================= - /** - * Applies the inverse algorithm to input data and returns a source estimate. - * - * @param[in] p_fiffEvoked Evoked data. - * @param[in] pick_normal If True, rather than pooling the orientations by taking the norm, only the. - * radial component is kept. This is only applied when working with loose orientations. - * - * @return the calculated source estimation. - */ - virtual MNELIB::MNESourceEstimate calculateInverse(const FIFFLIB::FiffEvoked &p_fiffEvoked, bool pick_normal = false) = 0; - - //========================================================================================================= - /** - * Applies the inverse algorithm to input data and returns a source estimate. - * - * @param[in] p_fiffEvoked Evoked data. - * @param[in] tmin Minimal time point. - * @param[in] tmin Time between two samples. - * @param[in] pick_normal If True, rather than pooling the orientations by taking the norm, only the. - * radial component is kept. This is only applied when working with loose orientations. - * - * @return the calculated source estimation. - */ - virtual MNELIB::MNESourceEstimate calculateInverse(const Eigen::MatrixXd &data, float tmin, float tstep, bool pick_normal = false) const = 0; - - //========================================================================================================= - /** - * Returns the algorithm name - * - * @return the algorithm name. - */ - virtual const char* getName() const = 0; - - //========================================================================================================= - /** - * Returns the current mne source space on which the inverse algorithm is performing on. - * Either from inverse operator (minimum norm estimate), or from forward solution (beamformers) - * - * @return the mne source space information. - */ - virtual const MNELIB::MNESourceSpaces& getSourceSpace() const = 0; -}; -} //NAMESPACE - -#endif // IINVERSEALGORITHM_H + // Init origin + r0 << 0.0f,0.0f,0.04f; + + mri_head_ident = false; + filter_spaces = true; + accurate = false; + fixed_ori = false; + include_meg = false; + include_eeg = false; + compute_grad = false; + mindist = 0.0f; + coord_frame = FIFFV_COORD_HEAD; + do_all = false; + nlabel = 0; + + eeg_sphere_rad = 0.09f; + scale_eeg_pos = false; + use_equiv_eeg = true; + use_threads = true; + + pFiffInfo = Q_NULLPTR; + meg_head_t = FiffCoordTrans(); +} diff --git a/src/libraries/fwd/computeFwd/compute_fwd_settings.h b/src/libraries/fwd/compute_fwd/compute_fwd_settings.h similarity index 86% rename from src/libraries/fwd/computeFwd/compute_fwd_settings.h rename to src/libraries/fwd/compute_fwd/compute_fwd_settings.h index 815255806bc..692d21df0e8 100644 --- a/src/libraries/fwd/computeFwd/compute_fwd_settings.h +++ b/src/libraries/fwd/compute_fwd/compute_fwd_settings.h @@ -30,12 +30,12 @@ * POSSIBILITY OF SUCH DAMAGE. * * - * @brief Compute Forward Setting class declaration. + * @brief ComputeFwdSettings class declaration. * */ -#ifndef COMPUTEFWDSETTINGS_H -#define COMPUTEFWDSETTINGS_H +#ifndef COMPUTE_FWD_SETTINGS_H +#define COMPUTE_FWD_SETTINGS_H //============================================================================================================= // INCLUDES @@ -72,9 +72,9 @@ namespace FWDLIB //============================================================================================================= /** - * Implements the compute forward setting parser + * Holds the parameter set for a forward-solution computation. * - * @brief Compute Forward setting implementation + * @brief Forward-computation parameter class. */ class FWDSHARED_EXPORT ComputeFwdSettings { @@ -88,15 +88,6 @@ class FWDSHARED_EXPORT ComputeFwdSettings */ explicit ComputeFwdSettings(); - //========================================================================================================= - /** - * Constructs Compute Forward Settings - * - * @param[in] argc (argument count) is an integer that indicates how many arguments were entered on the command line when the program was started. - * @param[in] argv (argument vector) is an array of pointers to arrays of character objects. The array objects are null-terminated strings, representing the arguments that were entered on the command line when the program was started. - */ - explicit ComputeFwdSettings(int *argc,char **argv); - //========================================================================================================= /** * Destructs the Compute Forward Settings @@ -145,12 +136,6 @@ class FWDSHARED_EXPORT ComputeFwdSettings private: void initMembers(); - void usage(char *name); - - static QString build_command_line(QString old, QString new_item); - - bool check_unrecognized_args(int argc, char **argv); - bool check_args (int *argc,char **argv); }; //============================================================================================================= @@ -158,4 +143,4 @@ class FWDSHARED_EXPORT ComputeFwdSettings //============================================================================================================= } //NAMESPACE -#endif // COMPUTEFWDSETTINGS_H +#endif // COMPUTE_FWD_SETTINGS_H diff --git a/src/libraries/fwd/fwd.h b/src/libraries/fwd/fwd.h new file mode 100644 index 00000000000..a5e5b720772 --- /dev/null +++ b/src/libraries/fwd/fwd.h @@ -0,0 +1,114 @@ +//============================================================================================================= +/** + * @file fwd.h + * @author Lorenz Esch ; + * Matti Hamalainen ; + * Christoph Dinh + * @since 0.1.0 + * @date March, 2026 + * + * @section LICENSE + * + * Copyright (C) 2026, Lorenz Esch, Matti Hamalainen, Christoph Dinh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided with the distribution. + * * Neither the name of MNE-CPP authors nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @brief Fwd class declaration, which provides static wrapper functions for the forward library. + */ +//============================================================================================================= + +#ifndef FWD_H +#define FWD_H + +//============================================================================================================= +// INCLUDES +//============================================================================================================= + +#include "fwd_global.h" +#include "fwd_forward_solution.h" + +#include + +//============================================================================================================= +// QT INCLUDES +//============================================================================================================= + +#include +#include + +//============================================================================================================= +// DEFINE NAMESPACE FWDLIB +//============================================================================================================= + +namespace FWDLIB +{ + +//============================================================================================================= +/** + * @brief The Fwd class provides static wrapper functions for the forward library. + */ +class FWDSHARED_EXPORT Fwd +{ +public: + + //========================================================================================================= + /** + * Destructor. + */ + virtual ~Fwd() + { } + + //========================================================================================================= + /** + * mne_read_forward_solution + * + * ### MNE toolbox root function ### + * + * Wrapper for the FwdForwardSolution::read static function + * + * Reads a forward solution from a fif file + * + * @param[in] p_IODevice A fiff IO device like a fiff QFile or QTCPSocket. + * @param[in, out] fwd A forward solution from a fif file. + * @param[in] force_fixed Force fixed source orientation mode? (optional). + * @param[in] surf_ori Use surface based source coordinate system? (optional). + * @param[in] include Include these channels (optional). + * @param[in] exclude Exclude these channels (optional). + * + * @return true if succeeded, false otherwise. + */ + static inline bool read_forward_solution(QIODevice& p_IODevice, + FwdForwardSolution& fwd, + bool force_fixed = false, + bool surf_ori = false, + const QStringList& include = FIFFLIB::defaultQStringList, + const QStringList& exclude = FIFFLIB::defaultQStringList) + { + return FwdForwardSolution::read(p_IODevice, + fwd, + force_fixed, + surf_ori, + include, + exclude); + } +}; + +} // NAMESPACE FWDLIB + +#endif // FWD_H diff --git a/src/libraries/fwd/fwd_bem_model.cpp b/src/libraries/fwd/fwd_bem_model.cpp index f8dbcdde8c5..dacca16d55c 100644 --- a/src/libraries/fwd/fwd_bem_model.cpp +++ b/src/libraries/fwd/fwd_bem_model.cpp @@ -46,7 +46,8 @@ #include #include "fwd_comp_data.h" -#include "fwd_bem_model.h" + +#include #include "fwd_thread_arg.h" @@ -63,9 +64,9 @@ #include -static float Qx[] = {1.0,0.0,0.0}; -static float Qy[] = {0.0,1.0,0.0}; -static float Qz[] = {0.0,0.0,1.0}; +static const Eigen::Vector3f Qx(1.0f, 0.0f, 0.0f); +static const Eigen::Vector3f Qy(0.0f, 1.0f, 0.0f); +static const Eigen::Vector3f Qz(0.0f, 0.0f, 1.0f); #ifndef TRUE #define TRUE 1 @@ -83,243 +84,54 @@ static float Qz[] = {0.0,0.0,1.0}; #define OK 0 #endif -#define X_40 0 -#define Y_40 1 -#define Z_40 2 - -#define VEC_DIFF_40(from,to,diff) {\ - (diff)[X_40] = (to)[X_40] - (from)[X_40];\ - (diff)[Y_40] = (to)[Y_40] - (from)[Y_40];\ - (diff)[Z_40] = (to)[Z_40] - (from)[Z_40];\ - } - -#define VEC_COPY_40(to,from) {\ - (to)[X_40] = (from)[X_40];\ - (to)[Y_40] = (from)[Y_40];\ - (to)[Z_40] = (from)[Z_40];\ - } - -#define VEC_DOT_40(x,y) ((x)[X_40]*(y)[X_40] + (x)[Y_40]*(y)[Y_40] + (x)[Z_40]*(y)[Z_40]) - -#define VEC_LEN_40(x) sqrt(VEC_DOT_40(x,x)) - -#define CROSS_PRODUCT_40(x,y,xy) {\ - (xy)[X_40] = (x)[Y_40]*(y)[Z_40]-(y)[Y_40]*(x)[Z_40];\ - (xy)[Y_40] = -((x)[X_40]*(y)[Z_40]-(y)[X_40]*(x)[Z_40]);\ - (xy)[Z_40] = (x)[X_40]*(y)[Y_40]-(y)[X_40]*(x)[Y_40];\ - } - -#define MALLOC_40(x,t) (t *)malloc((x)*sizeof(t)) - -#define ALLOC_CMATRIX_40(x,y) mne_cmatrix_40((x),(y)) - -#define FREE_40(x) if ((char *)(x) != NULL) free((char *)(x)) - -#define FREE_CMATRIX_40(m) mne_free_cmatrix_40((m)) - -void mne_free_cmatrix_40 (float **m) -{ - if (m) { - FREE_40(*m); - FREE_40(m); - } -} - -static void matrix_error_40(int kind, int nr, int nc) - -{ - if (kind == 1) - printf("Failed to allocate memory pointers for a %d x %d matrix\n",nr,nc); - else if (kind == 2) - printf("Failed to allocate memory for a %d x %d matrix\n",nr,nc); - else - printf("Allocation error for a %d x %d matrix\n",nr,nc); - if (sizeof(void *) == 4) { - printf("This is probably because you seem to be using a computer with 32-bit architecture.\n"); - printf("Please consider moving to a 64-bit platform."); - } - printf("Cannot continue. Sorry.\n"); - exit(1); -} - -float **mne_cmatrix_40 (int nr,int nc) - -{ - int i; - float **m; - float *whole; - - m = MALLOC_40(nr,float *); - if (!m) matrix_error_40(1,nr,nc); - whole = MALLOC_40(nr*nc,float); - if (!whole) matrix_error_40(2,nr,nc); - - for(i=0;i +inline void vec_diff(const T1& from, const T2& to, T3* diff) { - Eigen::MatrixXf eigen_mat = toFloatEigenMatrix_40(mat, dim, dim); - Eigen::MatrixXf eigen_mat_inv = eigen_mat.inverse(); - fromFloatEigenMatrix_40(eigen_mat_inv, mat); - return mat; + diff[X] = to[X] - from[X]; + diff[Y] = to[Y] - from[Y]; + diff[Z] = to[Z] - from[Z]; } -void mne_transpose_square_40(float **mat, int n) -/* - * In-place transpose of a square matrix - */ +template +inline void vec_copy(T1* to, const T2& from) { - int j,k; - float val; - - for (j = 1; j < n; j++) - for (k = 0; k < j; k++) { - val = mat[j][k]; - mat[j][k] = mat[k][j]; - mat[k][j] = val; - } - return; + to[X] = from[X]; + to[Y] = from[Y]; + to[Z] = from[Z]; } -float mne_dot_vectors_40(float *v1, - float *v2, - int nn) - +template +inline auto vec_dot(const T1& x, const T2& y) -> decltype(x[0]*y[0]) { -#ifdef BLAS - int one = 1; - float res = sdot(&nn,v1,&one,v2,&one); - return res; -#else - float res = 0.0; - int k; - - for (k = 0; k < nn; k++) - res = res + v1[k]*v2[k]; - return res; -#endif + return x[X]*y[X] + x[Y]*y[Y] + x[Z]*y[Z]; } -void mne_add_scaled_vector_to_40(float *v1,float scale, float *v2,int nn) - +template +inline double vec_len(const T& x) { -#ifdef BLAS - float fscale = scale; - int one = 1; - saxpy(&nn,&fscale,v1,&one,v2,&one); -#else - int k; - for (k = 0; k < nn; k++) - v2[k] = v2[k] + scale*v1[k]; -#endif - return; + return sqrt(static_cast(x[X]*x[X] + x[Y]*x[Y] + x[Z]*x[Z])); } -void mne_scale_vector_40(double scale,float *v,int nn) - +template +inline void cross_product(const T1& x, const T2& y, T3* xy) { -#ifdef BLAS - float fscale = scale; - int one = 1; - sscal(&nn,&fscale,v,&one); -#else - int k; - for (k = 0; k < nn; k++) - v[k] = v[k]*scale; -#endif -} - -#include -using namespace Eigen; - -float **mne_mat_mat_mult_40 (float **m1, - float **m2, - int d1, - int d2, - int d3) -/* Matrix multiplication - * result(d1 x d3) = m1(d1 x d2) * m2(d2 x d3) */ - -{ -#ifdef BLAS - float **result = ALLOC_CMATRIX_40(d1,d3); - char *transa = "N"; - char *transb = "N"; - float zero = 0.0; - float one = 1.0; - sgemm (transa,transb,&d3,&d1,&d2, - &one,m2[0],&d3,m1[0],&d2,&zero,result[0],&d3); - return (result); -#else - float **result = ALLOC_CMATRIX_40(d1,d3); - int j,k,p; - float sum; - - // TODO: Hack to include faster Eigen processing. This should eventually be replaced dwith pure Eigen logic. - MatrixXf a(d1,d2); - MatrixXf b(d2,d3); - - // ToDo use Eigen::Map - for (j = 0; j < d1; j++) { - for (k = 0; k < d2; k++) { - a(j,k) = m1[j][k]; - } - } - - for (j = 0; j < d2; j++) { - for (k = 0; k < d3; k++) { - b(j,k) = m2[j][k]; - } - } - - MatrixXf resultMat = a * b; - - for (j = 0; j < d1; j++) { - for (k = 0; k < d3; k++) { - result[j][k] = resultMat(j,k); - } - } - -// for (j = 0; j < d1; j++) -// for (k = 0; k < d3; k++) { -// sum = 0.0; -// for (p = 0; p < d2; p++) -// sum = sum + m1[j][p]*m2[p][k]; -// result[j][k] = sum; -// } - return (result); -#endif + xy[X] = x[Y]*y[Z] - y[Y]*x[Z]; + xy[Y] = -(x[X]*y[Z] - y[X]*x[Z]); + xy[Z] = x[X]*y[Y] - y[X]*x[Y]; } namespace FWDLIB @@ -345,7 +157,6 @@ static struct { #define BEM_SUFFIX "-bem.fif" #define BEM_SOL_SUFFIX "-bem-sol.fif" -//============================= misc_util.c ============================= static QString strip_from(const QString& s, const QString& suffix) { @@ -361,7 +172,6 @@ static QString strip_from(const QString& s, const QString& suffix) return res; } -//============================= mne_coord_transforms.c ============================= namespace FWDLIB { @@ -419,20 +229,12 @@ using namespace FWDLIB; //============================================================================================================= FwdBemModel::FwdBemModel() -:ntri (NULL) -,np (NULL) -,nsurf (0) -,sigma (NULL) -,gamma (NULL) -,source_mult(NULL) -,field_mult (NULL) -,bem_method (FWD_BEM_UNKNOWN) -,solution (NULL) -,nsol (0) -,head_mri_t () -,v0 (NULL) -,use_ip_approach(false) -,ip_approach_limit(FWD_BEM_IP_APPROACH_LIMIT) +: nsurf (0) +, bem_method (FWD_BEM_UNKNOWN) +, nsol (0) +, head_mri_t () +, ip_approach_limit(FWD_BEM_IP_APPROACH_LIMIT) +, use_ip_approach(false) { } @@ -440,14 +242,7 @@ FwdBemModel::FwdBemModel() FwdBemModel::~FwdBemModel() { - for (int k = 0; k < this->nsurf; k++) - delete this->surfs[k]; - FREE_40(this->ntri); - FREE_40(this->np); - FREE_40(this->sigma); - FREE_40(this->source_mult); - FREE_40(this->field_mult); - FREE_CMATRIX_40(this->gamma); + // surfs cleaned up automatically via shared_ptr this->fwd_bem_free_solution(); } @@ -455,13 +250,11 @@ FwdBemModel::~FwdBemModel() void FwdBemModel::fwd_bem_free_solution() { - FREE_CMATRIX_40(this->solution); this->solution = NULL; + this->solution.resize(0, 0); this->sol_name.clear(); - FREE_40(this->v0); this->v0 = NULL; + this->v0.resize(0); this->bem_method = FWD_BEM_UNKNOWN; this->nsol = 0; - - return; } //============================================================================================================= @@ -536,137 +329,109 @@ MNESurface *FwdBemModel::fwd_bem_find_surface(int kind) // } for (int k = 0; k < this->nsurf; k++) if (this->surfs[k]->id == kind) - return this->surfs[k]; + return this->surfs[k].get(); printf("Desired surface (%d = %s) not found.", kind,fwd_bem_explain_surface(kind).toUtf8().constData()); return NULL; } //============================================================================================================= -FwdBemModel *FwdBemModel::fwd_bem_load_surfaces(const QString &name, int *kinds, int nkind) +std::unique_ptr FwdBemModel::fwd_bem_load_surfaces(const QString &name, const std::vector& kinds) /* * Load a set of surfaces */ { - QList surfs;// = NULL; - float *sigma = NULL; - float *sigma1; - FwdBemModel *m = NULL; + std::vector> surfs; + const int nkind = static_cast(kinds.size()); + Eigen::VectorXf sigma_tmp(nkind); int j,k; if (nkind <= 0) { qCritical("No surfaces specified to fwd_bem_load_surfaces"); - return NULL; + return nullptr; } -// surfs = MALLOC_40(nkind,MNESurface*); - sigma = MALLOC_40(nkind,float); -// for (k = 0; k < nkind; k++) -// surfs[k] = NULL; - for (k = 0; k < nkind; k++) { - surfs.append(MNESurface::read_bem_surface(name,kinds[k],TRUE,sigma+k)); - if (surfs[k] == NULL) - goto bad; - if ((surfs[k] = MNESurface::read_bem_surface(name,kinds[k],TRUE,sigma+k)) == NULL) - goto bad; - if (sigma[k] < 0.0) { + float cond = -1.0f; + MNESurface* s = MNESurface::read_bem_surface(name,kinds[k],TRUE,&cond); + if (s == NULL) + return nullptr; + if (cond < 0.0) { qCritical("No conductivity available for surface %s",fwd_bem_explain_surface(kinds[k]).toUtf8().constData()); - goto bad; + delete s; + return nullptr; } - if (surfs[k]->coord_frame != FIFFV_COORD_MRI) { /* We make our life much easier with this */ + if (s->coord_frame != FIFFV_COORD_MRI) { qCritical("Surface %s not specified in MRI coordinates.",fwd_bem_explain_surface(kinds[k]).toUtf8().constData()); - goto bad; + delete s; + return nullptr; } + sigma_tmp[k] = cond; + surfs.push_back(std::shared_ptr(s)); } - m = new FwdBemModel; + auto m = std::make_unique(); m->surf_name = name; m->nsurf = nkind; - m->surfs = surfs; - m->sigma = sigma; - m->ntri = MALLOC_40(nkind,int); - m->np = MALLOC_40(nkind,int); - m->gamma = ALLOC_CMATRIX_40(nkind,nkind); - m->source_mult = MALLOC_40(nkind,float); - m->field_mult = MALLOC_40(nkind,float); + m->surfs = std::move(surfs); + m->sigma = sigma_tmp; + m->ntri.resize(nkind); + m->np.resize(nkind); + m->gamma.resize(nkind, nkind); + m->source_mult.resize(nkind); + m->field_mult.resize(nkind); /* - * Dirty trick for the zero conductivity outside - */ - sigma1 = MALLOC_40(nkind+1,float); - sigma1[0] = 0.0; - sigma = sigma1+1; - for (k = 0; k < m->nsurf; k++) - sigma[k] = m->sigma[k]; + * Build a shifted conductivity array with sigma[-1] = 0 (outside) + */ + Eigen::VectorXf sigma1(nkind + 1); + sigma1[0] = 0.0f; + sigma1.tail(nkind) = m->sigma; + // sigma[j] below refers to sigma1[j+1], sigma[j-1] to sigma1[j] /* - * Gamma factors and multipliers - */ + * Gamma factors and multipliers + */ for (j = 0; j < m->nsurf; j++) { m->ntri[j] = m->surfs[j]->ntri; m->np[j] = m->surfs[j]->np; - m->source_mult[j] = 2.0/(sigma[j]+sigma[j-1]); - m->field_mult[j] = sigma[j]-sigma[j-1]; + m->source_mult[j] = 2.0f / (sigma1[j+1] + sigma1[j]); + m->field_mult[j] = sigma1[j+1] - sigma1[j]; for (k = 0; k < m->nsurf; k++) - m->gamma[j][k] = (sigma[k]-sigma[k-1])/(sigma[j]+sigma[j-1]); + m->gamma(j, k) = (sigma1[k+1] - sigma1[k]) / (sigma1[j+1] + sigma1[j]); } - FREE_40(sigma1); return m; - -bad : { - FREE_40(sigma); - for (k = 0; k < surfs.size(); k++) - delete surfs[k]; -// FREE_40(surfs); - surfs.clear(); - return NULL; - } } //============================================================================================================= -FwdBemModel *FwdBemModel::fwd_bem_load_homog_surface(const QString &name) +std::unique_ptr FwdBemModel::fwd_bem_load_homog_surface(const QString &name) /* * Load surfaces for the homogeneous model */ { - int kinds[] = { FIFFV_BEM_SURF_ID_BRAIN }; - int nkind = 1; - - return fwd_bem_load_surfaces(name,kinds,nkind); + return fwd_bem_load_surfaces(name, {FIFFV_BEM_SURF_ID_BRAIN}); } //============================================================================================================= -FwdBemModel *FwdBemModel::fwd_bem_load_three_layer_surfaces(const QString &name) +std::unique_ptr FwdBemModel::fwd_bem_load_three_layer_surfaces(const QString &name) /* * Load surfaces for three-layer model */ { - int kinds[] = { FIFFV_BEM_SURF_ID_HEAD, FIFFV_BEM_SURF_ID_SKULL, FIFFV_BEM_SURF_ID_BRAIN }; - int nkind = 3; - - return fwd_bem_load_surfaces(name,kinds,nkind); + return fwd_bem_load_surfaces(name, {FIFFV_BEM_SURF_ID_HEAD, FIFFV_BEM_SURF_ID_SKULL, FIFFV_BEM_SURF_ID_BRAIN}); } //============================================================================================================= -int FwdBemModel::fwd_bem_load_solution(const QString &name, int bem_method, FwdBemModel *m) +int FwdBemModel::fwd_bem_load_solution(const QString &name, int bem_method) /* - * Load the potential solution matrix and attach it to the model: - * - * return values: - * - * TRUE found a suitable solution - * FALSE did not find a suitable solution - * FAIL error in reading the solution - * + * Load the potential solution matrix and attach it to the model */ { QFile file(name); FiffStream::SPtr stream(new FiffStream(&file)); - float **sol = NULL; FiffDirNode::SPtr bem_node; int method; FiffTag::SPtr t_pTag; @@ -717,54 +482,49 @@ int FwdBemModel::fwd_bem_load_solution(const QString &name, int bem_method, FwdB printf("Expected a two-dimensional solution matrix instead of a %d dimensional one",ndim); goto bad; } - for (k = 0, dim = 0; k < m->nsurf; k++) - dim = dim + ((method == FWD_BEM_LINEAR_COLL) ? m->surfs[k]->np : m->surfs[k]->ntri); + for (k = 0, dim = 0; k < nsurf; k++) + dim = dim + ((method == FWD_BEM_LINEAR_COLL) ? surfs[k]->np : surfs[k]->ntri); if (dims[0] != dim || dims[1] != dim) { printf("Expected a %d x %d solution matrix instead of a %d x %d one",dim,dim,dims[0],dims[1]); goto not_found; } MatrixXf tmp_sol = t_pTag->toFloatMatrix().transpose(); - sol = ALLOC_CMATRIX_40(tmp_sol.rows(),tmp_sol.cols()); - fromFloatEigenMatrix_40(tmp_sol, sol); nsol = dims[1]; } - if(m) - m->fwd_bem_free_solution(); - m->sol_name = name; - m->solution = sol; - m->nsol = nsol; - m->bem_method = method; + fwd_bem_free_solution(); + sol_name = name; + solution = t_pTag->toFloatMatrix().transpose(); + this->nsol = nsol; + this->bem_method = method; stream->close(); return TRUE; bad : { stream->close(); - FREE_CMATRIX_40(sol); return FAIL; } not_found : { stream->close(); - FREE_CMATRIX_40(sol); return FALSE; } } //============================================================================================================= -int FwdBemModel::fwd_bem_set_head_mri_t(FwdBemModel *m, const FiffCoordTrans &t) +int FwdBemModel::fwd_bem_set_head_mri_t(const FiffCoordTrans &t) /* * Set the coordinate transformation */ { if (t.from == FIFFV_COORD_HEAD && t.to == FIFFV_COORD_MRI) { - m->head_mri_t = t; + head_mri_t = t; return OK; } else if (t.from == FIFFV_COORD_MRI && t.to == FIFFV_COORD_HEAD) { - m->head_mri_t = t.inverted(); + head_mri_t = t.inverted(); return OK; } else { @@ -775,32 +535,16 @@ int FwdBemModel::fwd_bem_set_head_mri_t(FwdBemModel *m, const FiffCoordTrans &t) //============================================================================================================= -MNESurface* FwdBemModel::make_guesses(MNESurface* guess_surf, float guessrad, float *guess_r0, float grid, float exclude, float mindist) /* Exclude points closer than this to - * the guess boundary surface */ -/* - * Make a guess space inside a sphere - */ +std::unique_ptr FwdBemModel::make_guesses(MNESurface* guess_surf, float guessrad, const Eigen::Vector3f& guess_r0, float grid, float exclude, float mindist) { - char *bemname = NULL; + QString bemname; MNESurface* sphere = NULL; - MNESurface* res = NULL; + std::unique_ptr res; int k; float dist; - float r0[] = { 0.0, 0.0, 0.0 }; - - if (!guess_r0) - guess_r0 = r0; if (!guess_surf) { printf("Making a spherical guess space with radius %7.1f mm...\n",1000*guessrad); - //#ifdef USE_SHARE_PATH - // if ((bemname = mne_compose_mne_name("share/mne","icos.fif")) == NULL) - //#else - // if ((bemname = mne_compose_mne_name("setup/mne","icos.fif")) == NULL) - //#endif - // goto out; - - // QFile bemFile("/usr/pubsw/packages/mne/stable/share/mne/icos.fif"); QFile bemFile(QString(QCoreApplication::applicationDirPath() + "/../resources/general/surf2bem/icos.fif")); if ( !QCoreApplication::startingUp() ) @@ -813,17 +557,16 @@ MNESurface* FwdBemModel::make_guesses(MNESurface* guess_surf, float guessrad, fl goto out; } - bemname = MALLOC_40(strlen(bemFile.fileName().toUtf8().data())+1,char); - strcpy(bemname,bemFile.fileName().toUtf8().data()); + bemname = bemFile.fileName(); if ((sphere = MNESurface::read_bem_surface(bemname,9003,FALSE,NULL)) == NULL) goto out; for (k = 0; k < sphere->np; k++) { - dist = VEC_LEN_40(&sphere->rr(k,0)); - sphere->rr(k,X_40) = guessrad*sphere->rr(k,X_40)/dist + guess_r0[X_40]; - sphere->rr(k,Y_40) = guessrad*sphere->rr(k,Y_40)/dist + guess_r0[Y_40]; - sphere->rr(k,Z_40) = guessrad*sphere->rr(k,Z_40)/dist + guess_r0[Z_40]; + dist = vec_len(&sphere->rr(k,0)); + sphere->rr(k,X) = guessrad*sphere->rr(k,X)/dist + guess_r0[X]; + sphere->rr(k,Y) = guessrad*sphere->rr(k,Y)/dist + guess_r0[Y]; + sphere->rr(k,Z) = guessrad*sphere->rr(k,Z)/dist + guess_r0[Z]; } if (sphere->add_geometry_info(TRUE) == FAIL) goto out; @@ -835,10 +578,9 @@ MNESurface* FwdBemModel::make_guesses(MNESurface* guess_surf, float guessrad, fl mne_coord_frame_name_40(guess_surf->coord_frame).toUtf8().constData()); } printf("Filtering (grid = %6.f mm)...\n",1000*grid); - res = (MNESurface*)MNESourceSpace::make_volume_source_space(*guess_surf,grid,exclude,mindist); + res.reset((MNESurface*)MNESourceSpace::make_volume_source_space(*guess_surf,grid,exclude,mindist)); out : { - FREE_40(bemname); if(sphere) delete sphere; return res; @@ -847,24 +589,19 @@ out : { //============================================================================================================= -double FwdBemModel::calc_beta(double *rk, double *rk1) +double FwdBemModel::calc_beta(const Eigen::Vector3d& rk, const Eigen::Vector3d& rk1) { - double rkk1[3]; - double size; - double res; - - VEC_DIFF_40(rk,rk1,rkk1); - size = VEC_LEN_40(rkk1); + Eigen::Vector3d rkk1 = rk1 - rk; + double size = rkk1.norm(); - res = log((VEC_LEN_40(rk)*size + VEC_DOT_40(rk,rkk1))/ - (VEC_LEN_40(rk1)*size + VEC_DOT_40(rk1,rkk1)))/size; - return (res); + return log((rk.norm() * size + rk.dot(rkk1)) / + (rk1.norm() * size + rk1.dot(rkk1))) / size; } //============================================================================================================= -void FwdBemModel::lin_pot_coeff(float *from, MNETriangle* to, double omega[]) /* The final result */ +void FwdBemModel::lin_pot_coeff(const Eigen::Vector3f& from, MNETriangle& to, Eigen::Vector3d& omega) /* The final result */ /* * The linear potential matrix element computations */ @@ -876,7 +613,7 @@ void FwdBemModel::lin_pot_coeff(float *from, MNETriangle* to, double omega[]) /* double solid; /* The standard solid angle */ double vec_omega[3]; /* The cross-product integral */ double cross[3]; /* y1 x y2 */ - double triple; /* VEC_DOT_40(y1 x y2,y3) */ + double triple; /* vec_dot(y1 x y2,y3) */ double ss; double beta[3],bbeta[3]; int j,k; @@ -896,17 +633,17 @@ void FwdBemModel::lin_pot_coeff(float *from, MNETriangle* to, double omega[]) /* /* * The standard solid angle computation */ - VEC_DIFF_40(from,to->r1,y1); - VEC_DIFF_40(from,to->r2,y2); - VEC_DIFF_40(from,to->r3,y3); + vec_diff(from,to.r1,y1); + vec_diff(from,to.r2,y2); + vec_diff(from,to.r3,y3); - CROSS_PRODUCT_40(y1,y2,cross); - triple = VEC_DOT_40(cross,y3); + cross_product(y1,y2,cross); + triple = vec_dot(cross,y3); - l1 = VEC_LEN_40(y1); - l2 = VEC_LEN_40(y2); - l3 = VEC_LEN_40(y3); - ss = (l1*l2*l3+VEC_DOT_40(y1,y2)*l3+VEC_DOT_40(y1,y3)*l2+VEC_DOT_40(y2,y3)*l1); + l1 = vec_len(y1); + l2 = vec_len(y2); + l3 = vec_len(y3); + ss = (l1*l2*l3+vec_dot(y1,y2)*l3+vec_dot(y1,y3)*l2+vec_dot(y2,y3)*l1); solid = 2.0*atan2(triple,ss); if (std::fabs(solid) < solid_eps) { for (k = 0; k < 3; k++) @@ -917,7 +654,7 @@ void FwdBemModel::lin_pot_coeff(float *from, MNETriangle* to, double omega[]) /* * Calculate the magic vector vec_omega */ for (j = 0; j < 3; j++) - beta[j] = calc_beta(yy[j],yy[j+1]); + beta[j] = calc_beta(Eigen::Map(yy[j]),Eigen::Map(yy[j+1])); bbeta[0] = beta[2] - beta[0]; bbeta[1] = beta[0] - beta[1]; bbeta[2] = beta[1] - beta[2]; @@ -930,13 +667,13 @@ void FwdBemModel::lin_pot_coeff(float *from, MNETriangle* to, double omega[]) /* /* * Put it all together... */ - area2 = 2.0*to->area; + area2 = 2.0*to.area; n2 = 1.0/(area2*area2); for (k = 0; k < 3; k++) { - CROSS_PRODUCT_40(yy[k+1],yy[k-1],z); - VEC_DIFF_40(yy[k+1],yy[k-1],diff); - omega[k] = n2*(-area2*VEC_DOT_40(z,to->nn)*solid + - triple*VEC_DOT_40(diff,vec_omega)); + cross_product(yy[k+1],yy[k-1],z); + vec_diff(yy[k+1],yy[k-1],diff); + omega[k] = n2*(-area2*vec_dot(z,to.nn)*solid + + triple*vec_dot(diff,vec_omega)); } } #ifdef CHECK @@ -945,25 +682,25 @@ void FwdBemModel::lin_pot_coeff(float *from, MNETriangle* to, double omega[]) /* * * omega1 + omega2 + omega3 = solid */ - rel1 = (solid + omega[X_40]+omega[Y_40]+omega[Z_40])/solid; + rel1 = (solid + omega[0]+omega[1]+omega[2])/solid; /* * The other way of evaluating... */ for (j = 0; j < 3; j++) check[j] = 0; for (k = 0; k < 3; k++) { - CROSS_PRODUCT_40(to->nn[to],yy[k],z); + cross_product(to.nn,yy[k],z); for (j = 0; j < 3; j++) check[j] = check[j] + omega[k]*z[j]; } for (j = 0; j < 3; j++) check[j] = -area2*check[j]/triple; fprintf (stderr,"(%g,%g,%g) =? (%g,%g,%g)\n", - check[X_40],check[Y_40],check[Z_40], - vec_omega[X_40],vec_omega[Y_40],vec_omega[Z_40]); + check[0],check[1],check[2], + vec_omega[0],vec_omega[1],vec_omega[2]); for (j = 0; j < 3; j++) check[j] = check[j] - vec_omega[j]; - rel2 = sqrt(VEC_DOT_40(check,check)/VEC_DOT_40(vec_omega,vec_omega)); + rel2 = sqrt(vec_dot(check,check)/vec_dot(vec_omega,vec_omega)); fprintf (stderr,"err1 = %g, err2 = %g\n",100*rel1,100*rel2); #endif return; @@ -971,15 +708,14 @@ void FwdBemModel::lin_pot_coeff(float *from, MNETriangle* to, double omega[]) /* //============================================================================================================= -void FwdBemModel::correct_auto_elements(MNESurface *surf, float **mat) +void FwdBemModel::correct_auto_elements(MNESurface& surf, Eigen::MatrixXf& mat) /* * Improve auto-element approximation... */ { - float *row; float sum,miss; - int nnode = surf->np; - int ntri = surf->ntri; + int nnode = surf.np; + int ntri = surf.ntri; int nmemb; int j,k; float pi2 = 2.0*M_PI; @@ -987,52 +723,50 @@ void FwdBemModel::correct_auto_elements(MNESurface *surf, float **mat) #ifdef SIMPLE for (j = 0; j < nnode; j++) { - row = mat[j]; sum = 0.0; for (k = 0; k < nnode; k++) - sum = sum + row[k]; + sum = sum + mat(j,k); fprintf (stderr,"row %d sum = %g missing = %g\n",j+1,sum/pi2, 1.0-sum/pi2); - row[j] = pi2 - sum; + mat(j,j) = pi2 - sum; } #else for (j = 0; j < nnode; j++) { /* * How much is missing? */ - row = mat[j]; sum = 0.0; for (k = 0; k < nnode; k++) - sum = sum + row[k]; + sum = sum + mat(j,k); miss = pi2-sum; - nmemb = surf->nneighbor_tri[j]; + nmemb = surf.nneighbor_tri[j]; /* * The node itself receives one half */ - row[j] = miss/2.0; + mat(j,j) = miss/2.0; /* * The rest is divided evenly among the member nodes... */ miss = miss/(4.0*nmemb); - for (k = 0,tri = surf->tris.data(); k < ntri; k++,tri++) { + for (k = 0,tri = surf.tris.data(); k < ntri; k++,tri++) { if (tri->vert[0] == j) { - row[tri->vert[1]] = row[tri->vert[1]] + miss; - row[tri->vert[2]] = row[tri->vert[2]] + miss; + mat(j,tri->vert[1]) = mat(j,tri->vert[1]) + miss; + mat(j,tri->vert[2]) = mat(j,tri->vert[2]) + miss; } else if (tri->vert[1] == j) { - row[tri->vert[0]] = row[tri->vert[0]] + miss; - row[tri->vert[2]] = row[tri->vert[2]] + miss; + mat(j,tri->vert[0]) = mat(j,tri->vert[0]) + miss; + mat(j,tri->vert[2]) = mat(j,tri->vert[2]) + miss; } else if (tri->vert[2] == j) { - row[tri->vert[0]] = row[tri->vert[0]] + miss; - row[tri->vert[1]] = row[tri->vert[1]] + miss; + mat(j,tri->vert[0]) = mat(j,tri->vert[0]) + miss; + mat(j,tri->vert[1]) = mat(j,tri->vert[1]) + miss; } } /* * Just check it it out... * for (k = 0, sum = 0; k < nnode; k++) - sum = sum + row[k]; + sum = sum + mat(j,k); fprintf (stderr,"row %d sum = %g\n",j+1,sum/pi2); */ } @@ -1042,18 +776,14 @@ void FwdBemModel::correct_auto_elements(MNESurface *surf, float **mat) //============================================================================================================= -float **FwdBemModel::fwd_bem_lin_pot_coeff(const QList& surfs) +Eigen::MatrixXf FwdBemModel::fwd_bem_lin_pot_coeff(const std::vector& surfs) /* * Calculate the coefficients for linear collocation approach */ { - float **mat = NULL; - float **sub_mat = NULL; int np1,np2,ntri,np_tot,np_max; - float **nodes_ptr; MNETriangle* tri; - double omega[3]; - double *row = NULL; + Eigen::Vector3d omega; int j,k,p,q,c; int joff,koff; MNESurface* surf1; @@ -1065,16 +795,11 @@ float **FwdBemModel::fwd_bem_lin_pot_coeff(const QList& surfs) np_max = surfs[p]->np; } - mat = ALLOC_CMATRIX_40(np_tot,np_tot); - for (j = 0; j < np_tot; j++) - for (k = 0; k < np_tot; k++) - mat[j][k] = 0.0; - row = MALLOC_40(np_max,double); - sub_mat = MALLOC_40(np_max,float *); + Eigen::MatrixXf mat = Eigen::MatrixXf::Zero(np_tot, np_tot); + Eigen::VectorXd row(np_max); for (p = 0, joff = 0; p < surfs.size(); p++, joff = joff + np1) { surf1 = surfs[p]; np1 = surf1->np; - nodes_ptr = NULL; // UNUSED: was float** alias for (q = 0, koff = 0; q < surfs.size(); q++, koff = koff + np2) { surf2 = surfs[q]; np2 = surf2->np; @@ -1097,92 +822,93 @@ float **FwdBemModel::fwd_bem_lin_pot_coeff(const QList& surfs) /* * Otherwise do the hard job */ - lin_pot_coeff (&surf1->rr(j,0),tri,omega); + lin_pot_coeff(Eigen::Map(&surf1->rr(j,0)),*tri,omega); for (c = 0; c < 3; c++) row[tri->vert[c]] = row[tri->vert[c]] - omega[c]; } for (k = 0; k < np2; k++) - mat[j+joff][k+koff] = row[k]; + mat(j+joff,k+koff) = row[k]; } if (p == q) { - for (j = 0; j < np1; j++) - sub_mat[j] = mat[j+joff]+koff; - correct_auto_elements (surf1,sub_mat); + Eigen::MatrixXf sub_mat = mat.block(joff, koff, np1, np1); + correct_auto_elements(*surf1, sub_mat); + mat.block(joff, koff, np1, np1) = sub_mat; } printf("[done]\n"); } } - FREE_40(row); - FREE_40(sub_mat); - return(mat); + return mat; } //============================================================================================================= -int FwdBemModel::fwd_bem_linear_collocation_solution(FwdBemModel *m) +int FwdBemModel::fwd_bem_linear_collocation_solution() /* * Compute the linear collocation potential solution */ { - float **coeff = NULL; + Eigen::MatrixXf coeff; float ip_mult; int k; - if(m) - m->fwd_bem_free_solution(); + fwd_bem_free_solution(); + + // Extract raw surface pointers for coefficient functions + std::vector rawSurfs; + rawSurfs.reserve(nsurf); + for (auto& s : surfs) rawSurfs.push_back(s.get()); printf("\nComputing the linear collocation solution...\n"); fprintf (stderr,"\tMatrix coefficients...\n"); - if ((coeff = fwd_bem_lin_pot_coeff (m->surfs)) == NULL) + coeff = fwd_bem_lin_pot_coeff(rawSurfs); + if (coeff.size() == 0) goto bad; - for (k = 0, m->nsol = 0; k < m->nsurf; k++) - m->nsol += m->surfs[k]->np; + for (k = 0, nsol = 0; k < nsurf; k++) + nsol += surfs[k]->np; fprintf (stderr,"\tInverting the coefficient matrix...\n"); - if ((m->solution = fwd_bem_multi_solution (coeff,m->gamma,m->nsurf,m->np)) == NULL) + solution = fwd_bem_multi_solution(coeff, &gamma, nsurf, np); + if (solution.size() == 0) goto bad; /* * IP approach? */ - if ((m->nsurf == 3) && - (ip_mult = m->sigma[m->nsurf-2]/m->sigma[m->nsurf-1]) <= m->ip_approach_limit) { - float **ip_solution = NULL; + if ((nsurf == 3) && + (ip_mult = sigma[nsurf-2]/sigma[nsurf-1]) <= ip_approach_limit) { + Eigen::MatrixXf ip_solution; fprintf (stderr,"IP approach required...\n"); fprintf (stderr,"\tMatrix coefficients (homog)...\n"); - QList last_surfs; - last_surfs << m->surfs.last(); - if ((coeff = fwd_bem_lin_pot_coeff(last_surfs))== NULL)//m->surfs+m->nsurf-1,1)) == NULL) + std::vector last_surfs = { surfs.back().get() }; + coeff = fwd_bem_lin_pot_coeff(last_surfs); + if (coeff.size() == 0) goto bad; fprintf (stderr,"\tInverting the coefficient matrix (homog)...\n"); - if ((ip_solution = fwd_bem_homog_solution (coeff,m->surfs[m->nsurf-1]->np)) == NULL) + ip_solution = fwd_bem_homog_solution(coeff, surfs[nsurf-1]->np); + if (ip_solution.size() == 0) goto bad; fprintf (stderr,"\tModify the original solution to incorporate IP approach...\n"); - fwd_bem_ip_modify_solution(m->solution,ip_solution,ip_mult,m->nsurf,m->np); - FREE_CMATRIX_40(ip_solution); - + fwd_bem_ip_modify_solution(solution, ip_solution, ip_mult, nsurf, this->np); } - m->bem_method = FWD_BEM_LINEAR_COLL; + bem_method = FWD_BEM_LINEAR_COLL; printf("Solution ready.\n"); return OK; bad : { - if(m) - m->fwd_bem_free_solution(); - FREE_CMATRIX_40(coeff); + fwd_bem_free_solution(); return FAIL; } } //============================================================================================================= -float **FwdBemModel::fwd_bem_multi_solution(float **solids, float **gamma, int nsurf, int *ntri) /* Number of triangles or nodes on each surface */ +Eigen::MatrixXf FwdBemModel::fwd_bem_multi_solution(Eigen::MatrixXf& solids, const Eigen::MatrixXf *gamma, int nsurf, const Eigen::VectorXi& ntri) /* * Invert I - solids/(2*M_PI) * Take deflation into account @@ -1206,23 +932,24 @@ float **FwdBemModel::fwd_bem_multi_solution(float **solids, float **gamma, int n jup = ntri[p] + joff; for (q = 0, koff = 0; q < nsurf; q++) { kup = ntri[q] + koff; - mult = (gamma == NULL) ? pi2 : pi2*gamma[p][q]; + mult = (gamma == nullptr) ? pi2 : pi2 * (*gamma)(p, q); for (j = joff; j < jup; j++) for (k = koff; k < kup; k++) - solids[j][k] = defl - solids[j][k]*mult; + solids(j,k) = defl - solids(j,k)*mult; koff = kup; } joff = jup; } for (k = 0; k < ntot; k++) - solids[k][k] = solids[k][k] + 1.0; + solids(k,k) = solids(k,k) + 1.0; - return (mne_lu_invert_40(solids,ntot)); + Eigen::MatrixXf result = solids.inverse(); + return result; } //============================================================================================================= -float **FwdBemModel::fwd_bem_homog_solution(float **solids, int ntri) +Eigen::MatrixXf FwdBemModel::fwd_bem_homog_solution(Eigen::MatrixXf& solids, int ntri) /* * Invert I - solids/(2*M_PI) * Take deflation into account @@ -1230,12 +957,12 @@ float **FwdBemModel::fwd_bem_homog_solution(float **solids, int ntri) * This is the homogeneous model case */ { - return fwd_bem_multi_solution (solids,NULL,1,&ntri); + return fwd_bem_multi_solution (solids,nullptr,1,Eigen::VectorXi::Constant(1,ntri)); } //============================================================================================================= -void FwdBemModel::fwd_bem_ip_modify_solution(float **solution, float **ip_solution, float ip_mult, int nsurf, int *ntri) /* Number of triangles (nodes) on each surface */ +void FwdBemModel::fwd_bem_ip_modify_solution(Eigen::MatrixXf &solution, Eigen::MatrixXf& ip_solution, float ip_mult, int nsurf, const Eigen::VectorXi &ntri) /* * Modify the solution according to the IP approach */ @@ -1243,88 +970,69 @@ void FwdBemModel::fwd_bem_ip_modify_solution(float **solution, float **ip_soluti int s; int j,k,joff,koff,ntot,nlast; float mult; - float *row = NULL; - float **sub = NULL; for (s = 0, koff = 0; s < nsurf-1; s++) koff = koff + ntri[s]; nlast = ntri[nsurf-1]; ntot = koff + nlast; - row = MALLOC_40(nlast,float); - sub = MALLOC_40(ntot,float *); + Eigen::VectorXf row(nlast); mult = (1.0 + ip_mult)/ip_mult; printf("\t\tCombining..."); -#ifndef OLD printf("t "); - mne_transpose_square_40(ip_solution,nlast); -#endif + ip_solution.transposeInPlace(); + for (s = 0, joff = 0; s < nsurf; s++) { printf("%d3 ",s+1); /* - * Pick the correct submatrix - */ - for (j = 0; j < ntri[s]; j++) - sub[j] = solution[j+joff]+koff; - /* - * Multiply - */ -#ifdef OLD + * For each row in this surface block, compute dot products + * with the transposed ip_solution and subtract 2x the result + */ for (j = 0; j < ntri[s]; j++) { for (k = 0; k < nlast; k++) { - res = mne_dot_vectors_skip_skip(sub[j],1,ip_solution[0]+k,nlast,nlast); - row[k] = sub[j][k] - 2.0*res; + row[k] = solution.row(j + joff).segment(koff, nlast).dot(ip_solution.row(k).head(nlast)); } - for (k = 0; k < nlast; k++) - sub[j][k] = row[k]; + solution.row(j + joff).segment(koff, nlast) -= 2.0f * row.transpose(); } -#else - for (j = 0; j < ntri[s]; j++) { - for (k = 0; k < nlast; k++) - row[k] = mne_dot_vectors_40(sub[j],ip_solution[k],nlast); - mne_add_scaled_vector_to_40(row,-2.0,sub[j],nlast); - } -#endif - joff = joff+ntri[s]; + joff = joff + ntri[s]; } -#ifndef OLD + printf("t "); - mne_transpose_square_40(ip_solution,nlast); -#endif + ip_solution.transposeInPlace(); + printf("33 "); /* * The lower right corner is a special case */ for (j = 0; j < nlast; j++) for (k = 0; k < nlast; k++) - sub[j][k] = sub[j][k] + mult*ip_solution[j][k]; + solution(j + koff, k + koff) += mult * ip_solution(j,k); /* * Final scaling */ printf("done.\n\t\tScaling..."); - mne_scale_vector_40(ip_mult,solution[0],ntot*ntot); + solution *= ip_mult; printf("done.\n"); - FREE_40(row); FREE_40(sub); return; } //============================================================================================================= -int FwdBemModel::fwd_bem_check_solids(float **angles, int ntri1, int ntri2, float desired) +int FwdBemModel::fwd_bem_check_solids(const Eigen::MatrixXf& angles, int ntri1, int ntri2, float desired) /* * Check the angle computations */ { - float *sums = MALLOC_40(ntri1,float); float sum; int j,k; int res = 0; + Eigen::VectorXf sums(ntri1); for (j = 0; j < ntri1; j++) { sum = 0; for (k = 0; k < ntri2; k++) - sum = sum + angles[j][k]; + sum = sum + angles(j,k); sums[j] = sum/(2*M_PI); } for (j = 0; j < ntri1; j++) @@ -1340,13 +1048,12 @@ int FwdBemModel::fwd_bem_check_solids(float **angles, int ntri1, int ntri2, floa res = -1; break; } - FREE_40(sums); return res; } //============================================================================================================= -float **FwdBemModel::fwd_bem_solid_angles(const QList& surfs) +Eigen::MatrixXf FwdBemModel::fwd_bem_solid_angles(const std::vector& surfs) /* * Compute the solid angle matrix */ @@ -1357,16 +1064,13 @@ float **FwdBemModel::fwd_bem_solid_angles(const QList& surfs) int ntri1,ntri2,ntri_tot; int j,k,p,q; int joff,koff; - float **solids; float result; - float **sub_solids = NULL; float desired; for (p = 0,ntri_tot = 0; p < surfs.size(); p++) ntri_tot += surfs[p]->ntri; - sub_solids = MALLOC_40(ntri_tot,float *); - solids = ALLOC_CMATRIX_40(ntri_tot,ntri_tot); + Eigen::MatrixXf solids = Eigen::MatrixXf::Zero(ntri_tot, ntri_tot); for (p = 0, joff = 0; p < surfs.size(); p++, joff = joff + ntri1) { surf1 = surfs[p]; ntri1 = surf1->ntri; @@ -1380,10 +1084,8 @@ float **FwdBemModel::fwd_bem_solid_angles(const QList& surfs) result = 0.0; else result = MNESurfaceOrVolume::solid_angle (surf1->tris[j].cent,*tri); - solids[j+joff][k+koff] = result; + solids(j+joff,k+koff) = result; } - for (j = 0; j < ntri1; j++) - sub_solids[j] = solids[j+joff]+koff; printf("[done]\n"); if (p == q) desired = 1; @@ -1391,81 +1093,83 @@ float **FwdBemModel::fwd_bem_solid_angles(const QList& surfs) desired = 0; else desired = 2; - if (fwd_bem_check_solids(sub_solids,ntri1,ntri2,desired) == FAIL) { - FREE_CMATRIX_40(solids); - FREE_40(sub_solids); - return NULL; + Eigen::MatrixXf sub_block = solids.block(joff, koff, ntri1, ntri2); + if (fwd_bem_check_solids(sub_block,ntri1,ntri2,desired) == FAIL) { + return Eigen::MatrixXf(); } } } - FREE_40(sub_solids); - return (solids); + return solids; } //============================================================================================================= -int FwdBemModel::fwd_bem_constant_collocation_solution(FwdBemModel *m) +int FwdBemModel::fwd_bem_constant_collocation_solution() /* * Compute the solution for the constant collocation approach */ { - float **solids = NULL; + Eigen::MatrixXf solids; int k; float ip_mult; - if(m) - m->fwd_bem_free_solution(); + fwd_bem_free_solution(); + + // Extract raw surface pointers for coefficient functions + std::vector rawSurfs; + rawSurfs.reserve(nsurf); + for (auto& s : surfs) rawSurfs.push_back(s.get()); printf("\nComputing the constant collocation solution...\n"); printf("\tSolid angles...\n"); - if ((solids = fwd_bem_solid_angles(m->surfs)) == NULL) + solids = fwd_bem_solid_angles(rawSurfs); + if (solids.size() == 0) goto bad; - for (k = 0, m->nsol = 0; k < m->nsurf; k++) - m->nsol += m->surfs[k]->ntri; + for (k = 0, nsol = 0; k < nsurf; k++) + nsol += surfs[k]->ntri; fprintf (stderr,"\tInverting the coefficient matrix...\n"); - if ((m->solution = fwd_bem_multi_solution (solids,m->gamma,m->nsurf,m->ntri)) == NULL) + solution = fwd_bem_multi_solution(solids, &gamma, nsurf, ntri); + if (solution.size() == 0) goto bad; /* * IP approach? */ - if ((m->nsurf == 3) && - (ip_mult = m->sigma[m->nsurf-2]/m->sigma[m->nsurf-1]) <= m->ip_approach_limit) { - float **ip_solution = NULL; + if ((nsurf == 3) && + (ip_mult = sigma[nsurf-2]/sigma[nsurf-1]) <= ip_approach_limit) { + Eigen::MatrixXf ip_solution; fprintf (stderr,"IP approach required...\n"); fprintf (stderr,"\tSolid angles (homog)...\n"); - QList last_surfs; - last_surfs << m->surfs.last(); - if ((solids = fwd_bem_solid_angles (last_surfs)) == NULL)//m->surfs+m->nsurf-1,1)) == NULL) + std::vector last_surfs = { surfs.back().get() }; + solids = fwd_bem_solid_angles(last_surfs); + if (solids.size() == 0) goto bad; fprintf (stderr,"\tInverting the coefficient matrix (homog)...\n"); - if ((ip_solution = fwd_bem_homog_solution (solids,m->surfs[m->nsurf-1]->ntri)) == NULL) + ip_solution = fwd_bem_homog_solution(solids, surfs[nsurf-1]->ntri); + if (ip_solution.size() == 0) goto bad; fprintf (stderr,"\tModify the original solution to incorporate IP approach...\n"); - fwd_bem_ip_modify_solution(m->solution,ip_solution,ip_mult,m->nsurf,m->ntri); - FREE_CMATRIX_40(ip_solution); + fwd_bem_ip_modify_solution(solution, ip_solution, ip_mult, nsurf, this->ntri); } - m->bem_method = FWD_BEM_CONSTANT_COLL; + bem_method = FWD_BEM_CONSTANT_COLL; fprintf (stderr,"Solution ready.\n"); return OK; bad : { - if(m) - m->fwd_bem_free_solution(); - FREE_CMATRIX_40(solids); + fwd_bem_free_solution(); return FAIL; } } //============================================================================================================= -int FwdBemModel::fwd_bem_compute_solution(FwdBemModel *m, int bem_method) +int FwdBemModel::fwd_bem_compute_solution(int bem_method) /* * Compute the solution */ @@ -1474,36 +1178,29 @@ int FwdBemModel::fwd_bem_compute_solution(FwdBemModel *m, int bem_method) * Compute the solution */ if (bem_method == FWD_BEM_LINEAR_COLL) - return fwd_bem_linear_collocation_solution(m); + return fwd_bem_linear_collocation_solution(); else if (bem_method == FWD_BEM_CONSTANT_COLL) - return fwd_bem_constant_collocation_solution(m); + return fwd_bem_constant_collocation_solution(); - if(m) - m->fwd_bem_free_solution(); + fwd_bem_free_solution(); printf ("Unknown BEM method: %d\n",bem_method); return FAIL; } //============================================================================================================= -int FwdBemModel::fwd_bem_load_recompute_solution(const QString& name, int bem_method, int force_recompute, FwdBemModel *m) +int FwdBemModel::fwd_bem_load_recompute_solution(const QString& name, int bem_method, int force_recompute) /* * Load or recompute the potential solution matrix */ { int solres; - if (!m) { - printf ("No model specified for fwd_bem_load_recompute_solution"); - return FAIL; - } - if (!force_recompute) { - if(m) - m->fwd_bem_free_solution(); - solres = fwd_bem_load_solution(name,bem_method,m); + fwd_bem_free_solution(); + solres = fwd_bem_load_solution(name,bem_method); if (solres == TRUE) { - printf("\nLoaded %s BEM solution from %s\n",fwd_bem_explain_method(m->bem_method).toUtf8().constData(),name.toUtf8().constData()); + printf("\nLoaded %s BEM solution from %s\n",fwd_bem_explain_method(this->bem_method).toUtf8().constData(),name.toUtf8().constData()); return OK; } else if (solres == FAIL) @@ -1515,43 +1212,39 @@ int FwdBemModel::fwd_bem_load_recompute_solution(const QString& name, int bem_me } if (bem_method == FWD_BEM_UNKNOWN) bem_method = FWD_BEM_LINEAR_COLL; - return fwd_bem_compute_solution(m,bem_method); + return fwd_bem_compute_solution(bem_method); } //============================================================================================================= -float FwdBemModel::fwd_bem_inf_field(float *rd, float *Q, float *rp, float *dir) /* Which field component */ +float FwdBemModel::fwd_bem_inf_field(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, const Eigen::Vector3f& rp, const Eigen::Vector3f& dir) /* * Infinite-medium magnetic field * (without \mu_0/4\pi) */ { - float diff[3],diff2,cross[3]; - - VEC_DIFF_40(rd,rp,diff); - diff2 = VEC_DOT_40(diff,diff); - CROSS_PRODUCT_40(Q,diff,cross); + Eigen::Vector3f diff = rp - rd; + float diff2 = diff.squaredNorm(); + Eigen::Vector3f cr = Q.cross(diff); - return (VEC_DOT_40(cross,dir)/(diff2*sqrt(diff2))); + return cr.dot(dir) / (diff2 * std::sqrt(diff2)); } //============================================================================================================= -float FwdBemModel::fwd_bem_inf_pot(float *rd, float *Q, float *rp) /* Potential point */ +float FwdBemModel::fwd_bem_inf_pot(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, const Eigen::Vector3f& rp) /* * The infinite medium potential */ { - float diff[3]; - float diff2; - VEC_DIFF_40(rd,rp,diff); - diff2 = VEC_DOT_40(diff,diff); - return (VEC_DOT_40(Q,diff)/(4.0*M_PI*diff2*sqrt(diff2))); + Eigen::Vector3f diff = rp - rd; + float diff2 = diff.squaredNorm(); + return Q.dot(diff) / (4.0 * M_PI * diff2 * std::sqrt(diff2)); } //============================================================================================================= -int FwdBemModel::fwd_bem_specify_els(FwdBemModel* m, FwdCoilSet *els) +int FwdBemModel::fwd_bem_specify_els(FwdCoilSet *els) /* * Set up for computing the solution at a set of electrodes */ @@ -1559,80 +1252,70 @@ int FwdBemModel::fwd_bem_specify_els(FwdBemModel* m, FwdCoilSet *els) FwdCoil* el; MNESurface* scalp; int k,p,q,v; - float *one_sol,*pick_sol; float r[3],w[3],dist; int best; MNETriangle* tri; float x,y,z; - FwdBemSolution* sol; + FwdBemSolution* sol = nullptr; - if (!m) { - printf("Model missing in fwd_bem_specify_els"); - goto bad; - } - if (!m->solution) { + if (solution.size() == 0) { printf("Solution not computed in fwd_bem_specify_els"); goto bad; } - if (!els || els->ncoil == 0) + if (!els || els->ncoil() == 0) return OK; els->fwd_free_coil_set_user_data(); /* * Hard work follows */ - els->user_data = sol = new FwdBemSolution(); - els->user_data_free = FwdBemSolution::fwd_bem_free_coil_solution; + els->user_data = std::make_unique(); + sol = els->user_data.get(); - sol->ncoil = els->ncoil; - sol->np = m->nsol; - sol->solution = ALLOC_CMATRIX_40(sol->ncoil,sol->np); + sol->ncoil = els->ncoil(); + sol->np = nsol; + sol->solution = Eigen::MatrixXf::Zero(sol->ncoil,sol->np); /* * Go through all coils */ - for (k = 0; k < els->ncoil; k++) { - el = els->coils[k]; - one_sol = sol->solution[k]; - for (q = 0; q < m->nsol; q++) - one_sol[q] = 0.0; - scalp = m->surfs[0]; + for (k = 0; k < els->ncoil(); k++) { + el = els->coils[k].get(); + scalp = surfs[0].get(); /* * Go through all 'integration points' */ for (p = 0; p < el->np; p++) { - VEC_COPY_40(r,el->rmag[p]); - if (!m->head_mri_t.isEmpty()) - FiffCoordTrans::apply_trans(r,m->head_mri_t,FIFFV_MOVE); + vec_copy(r,&el->rmag(p, 0)); + if (!head_mri_t.isEmpty()) + FiffCoordTrans::apply_trans(r,head_mri_t,FIFFV_MOVE); best = scalp->project_to_surface(nullptr,Eigen::Map(r),dist); if (best < 0) { printf("One of the electrodes could not be projected onto the scalp surface. How come?"); goto bad; } - if (m->bem_method == FWD_BEM_CONSTANT_COLL) { + if (bem_method == FWD_BEM_CONSTANT_COLL) { /* - * Simply pick the value at the triangle - */ - pick_sol = m->solution[best]; - for (q = 0; q < m->nsol; q++) - one_sol[q] += el->w[p]*pick_sol[q]; + * Simply pick the value at the triangle + */ + for (q = 0; q < nsol; q++) + sol->solution(k, q) += el->w[p] * solution(best, q); } - else if (m->bem_method == FWD_BEM_LINEAR_COLL) { + else if (bem_method == FWD_BEM_LINEAR_COLL) { /* - * Calculate a linear interpolation between the vertex values - */ + * Calculate a linear interpolation between the vertex values + */ tri = &scalp->tris[best]; scalp->triangle_coords(Eigen::Map(r),best,x,y,z); - w[X_40] = el->w[p]*(1.0 - x - y); - w[Y_40] = el->w[p]*x; - w[Z_40] = el->w[p]*y; + w[0] = el->w[p]*(1.0 - x - y); + w[1] = el->w[p]*x; + w[2] = el->w[p]*y; for (v = 0; v < 3; v++) { - pick_sol = m->solution[tri->vert[v]]; - for (q = 0; q < m->nsol; q++) - one_sol[q] += w[v]*pick_sol[q]; + for (q = 0; q < nsol; q++) + sol->solution(k, q) += w[v] * solution(tri->vert[v], q); } } else { - printf("Unknown BEM approximation method : %d\n",m->bem_method); + printf("Unknown BEM approximation method : %d\n",bem_method); goto bad; } } @@ -1647,7 +1330,7 @@ bad : { //============================================================================================================= -void FwdBemModel::fwd_bem_pot_grad_calc(float *rd, float *Q, FwdBemModel* m, FwdCoilSet* els, int all_surfs, float *xgrad, float *ygrad, float *zgrad) +void FwdBemModel::fwd_bem_pot_grad_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet* els, int all_surfs, Eigen::VectorXf& xgrad, Eigen::VectorXf& ygrad, Eigen::VectorXf& zgrad) /* * Compute the potentials due to a current dipole */ @@ -1656,60 +1339,52 @@ void FwdBemModel::fwd_bem_pot_grad_calc(float *rd, float *Q, FwdBemModel* m, Fwd int ntri; int s,k,p,nsol,pp; float mult; - float *v0,ee[3]; - float **solution; - float mri_rd[3],mri_Q[3]; - - float *grads[3]; - float *grad; - - grads[0] = xgrad; - grads[1] = ygrad; - grads[2] = zgrad; - - if (!m->v0) - m->v0 = MALLOC_40(m->nsol,float); - v0 = m->v0; - - VEC_COPY_40(mri_rd,rd); - VEC_COPY_40(mri_Q,Q); - if (!m->head_mri_t.isEmpty()) { - FiffCoordTrans::apply_trans(mri_rd,m->head_mri_t,FIFFV_MOVE); - FiffCoordTrans::apply_trans(mri_Q,m->head_mri_t,FIFFV_NO_MOVE); - } - for (pp = 0; pp < 3; pp++) { - grad = grads[pp]; - - for (p = 0; p < 3; p++) - ee[p] = p == pp ? 1.0 : 0.0; - if (!m->head_mri_t.isEmpty()) - FiffCoordTrans::apply_trans(ee,m->head_mri_t,FIFFV_NO_MOVE); - - for (s = 0, p = 0; s < m->nsurf; s++) { - ntri = m->surfs[s]->ntri; - tri = m->surfs[s]->tris.data(); - mult = m->source_mult[s]; + Eigen::Vector3f ee; + Eigen::Vector3f mri_rd = rd; + Eigen::Vector3f mri_Q = Q; + + Eigen::VectorXf* grads[] = {&xgrad, &ygrad, &zgrad}; + + if (v0.size() == 0) + v0.resize(this->nsol); + float* v0p = v0.data(); + + if (!head_mri_t.isEmpty()) { + FiffCoordTrans::apply_trans(mri_rd.data(),head_mri_t,FIFFV_MOVE); + FiffCoordTrans::apply_trans(mri_Q.data(),head_mri_t,FIFFV_NO_MOVE); + } + for (pp = X; pp <= Z; pp++) { + Eigen::VectorXf& grad = *grads[pp]; + + ee = Eigen::Vector3f::Unit(pp); + if (!head_mri_t.isEmpty()) + FiffCoordTrans::apply_trans(ee.data(),head_mri_t,FIFFV_NO_MOVE); + + for (s = 0, p = 0; s < nsurf; s++) { + ntri = surfs[s]->ntri; + tri = surfs[s]->tris.data(); + mult = source_mult[s]; for (k = 0; k < ntri; k++, tri++) - v0[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,tri->cent.data(),ee); + v0p[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,tri->cent,ee); } if (els) { - FwdBemSolution* sol = (FwdBemSolution*)els->user_data; - solution = sol->solution; - nsol = sol->ncoil; + FwdBemSolution* sol = els->user_data.get(); + nsol = sol->ncoil; + for (k = 0; k < nsol; k++) + grad[k] = sol->solution.row(k).dot(v0); } else { - solution = m->solution; - nsol = all_surfs ? m->nsol : m->surfs[0]->ntri; + nsol = all_surfs ? this->nsol : surfs[0]->ntri; + for (k = 0; k < nsol; k++) + grad[k] = solution.row(k).dot(v0); } - for (k = 0; k < nsol; k++) - grad[k] = mne_dot_vectors_40(solution[k],v0,m->nsol); } return; } //============================================================================================================= -void FwdBemModel::fwd_bem_lin_pot_calc(float *rd, float *Q, FwdBemModel *m, FwdCoilSet *els, int all_surfs, float *pot) /* Put the result here */ +void FwdBemModel::fwd_bem_lin_pot_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet *els, int all_surfs, Eigen::VectorXf& pot) /* * Compute the potentials due to a current dipole * using the linear potential approximation @@ -1718,44 +1393,41 @@ void FwdBemModel::fwd_bem_lin_pot_calc(float *rd, float *Q, FwdBemModel *m, FwdC float *rr_row; int np; int s,k,p,nsol; - float mult,mri_rd[3],mri_Q[3]; - - float *v0; - float **solution; + float mult; + Eigen::Vector3f mri_rd = rd; + Eigen::Vector3f mri_Q = Q; - if (!m->v0) - m->v0 = MALLOC_40(m->nsol,float); - v0 = m->v0; + if (v0.size() == 0) + v0.resize(this->nsol); + float* v0p = v0.data(); - VEC_COPY_40(mri_rd,rd); - VEC_COPY_40(mri_Q,Q); - if (!m->head_mri_t.isEmpty()) { - FiffCoordTrans::apply_trans(mri_rd,m->head_mri_t,FIFFV_MOVE); - FiffCoordTrans::apply_trans(mri_Q,m->head_mri_t,FIFFV_NO_MOVE); + if (!head_mri_t.isEmpty()) { + FiffCoordTrans::apply_trans(mri_rd.data(),head_mri_t,FIFFV_MOVE); + FiffCoordTrans::apply_trans(mri_Q.data(),head_mri_t,FIFFV_NO_MOVE); } - for (s = 0, p = 0; s < m->nsurf; s++) { - np = m->surfs[s]->np; - mult = m->source_mult[s]; + for (s = 0, p = 0; s < nsurf; s++) { + np = surfs[s]->np; + mult = source_mult[s]; for (k = 0; k < np; k++) - v0[p++] = mult*fwd_bem_inf_pot(mri_rd,mri_Q,&m->surfs[s]->rr(k,0)); + v0p[p++] = mult*fwd_bem_inf_pot(mri_rd,mri_Q,Eigen::Map(&surfs[s]->rr(k,0))); } if (els) { - FwdBemSolution* sol = (FwdBemSolution*)els->user_data; - solution = sol->solution; - nsol = sol->ncoil; + FwdBemSolution* sol = els->user_data.get(); + nsol = sol->ncoil; + for (k = 0; k < nsol; k++) + pot[k] = sol->solution.row(k).dot(v0); } else { - solution = m->solution; - nsol = all_surfs ? m->nsol : m->surfs[0]->np; + nsol = all_surfs ? this->nsol : surfs[0]->np; + for (k = 0; k < nsol; k++) + pot[k] = solution.row(k).dot(v0); } - for (k = 0; k < nsol; k++) - pot[k] = mne_dot_vectors_40(solution[k],v0,m->nsol); return; } //============================================================================================================= -void FwdBemModel::fwd_bem_lin_pot_grad_calc(float *rd, float *Q, FwdBemModel *m, FwdCoilSet *els, int all_surfs, float *xgrad, float *ygrad, float *zgrad) +void FwdBemModel::fwd_bem_lin_pot_grad_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet *els, int all_surfs, Eigen::VectorXf& xgrad, Eigen::VectorXf& ygrad, Eigen::VectorXf& zgrad) /* * Compute the derivaties of potentials due to a current dipole with respect to the dipole position * using the linear potential approximation @@ -1763,60 +1435,52 @@ void FwdBemModel::fwd_bem_lin_pot_grad_calc(float *rd, float *Q, FwdBemModel *m, { int np; int s,k,p,nsol,pp; - float mult,mri_rd[3],mri_Q[3]; - - float *v0,ee[3]; - float **solution; - - float *grads[3]; - float *grad; + float mult; + Eigen::Vector3f mri_rd = rd; + Eigen::Vector3f mri_Q = Q; + Eigen::Vector3f ee; - grads[0] = xgrad; - grads[1] = ygrad; - grads[2] = zgrad; + Eigen::VectorXf* grads[] = {&xgrad, &ygrad, &zgrad}; - if (!m->v0) - m->v0 = MALLOC_40(m->nsol,float); - v0 = m->v0; + if (v0.size() == 0) + v0.resize(this->nsol); + float* v0p = v0.data(); - VEC_COPY_40(mri_rd,rd); - VEC_COPY_40(mri_Q,Q); - if (!m->head_mri_t.isEmpty()) { - FiffCoordTrans::apply_trans(mri_rd,m->head_mri_t,FIFFV_MOVE); - FiffCoordTrans::apply_trans(mri_Q,m->head_mri_t,FIFFV_NO_MOVE); + if (!head_mri_t.isEmpty()) { + FiffCoordTrans::apply_trans(mri_rd.data(),head_mri_t,FIFFV_MOVE); + FiffCoordTrans::apply_trans(mri_Q.data(),head_mri_t,FIFFV_NO_MOVE); } - for (pp = 0; pp < 3; pp++) { - grad = grads[pp]; + for (pp = X; pp <= Z; pp++) { + Eigen::VectorXf& grad = *grads[pp]; - for (p = 0; p < 3; p++) - ee[p] = p == pp ? 1.0 : 0.0; - if (!m->head_mri_t.isEmpty()) - FiffCoordTrans::apply_trans(ee,m->head_mri_t,FIFFV_NO_MOVE); + ee = Eigen::Vector3f::Unit(pp); + if (!head_mri_t.isEmpty()) + FiffCoordTrans::apply_trans(ee.data(),head_mri_t,FIFFV_NO_MOVE); - for (s = 0, p = 0; s < m->nsurf; s++) { - np = m->surfs[s]->np; - mult = m->source_mult[s]; + for (s = 0, p = 0; s < nsurf; s++) { + np = surfs[s]->np; + mult = source_mult[s]; for (k = 0; k < np; k++) - v0[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,&m->surfs[s]->rr(k,0),ee); + v0p[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,Eigen::Map(&surfs[s]->rr(k,X)),ee); } if (els) { - FwdBemSolution* sol = (FwdBemSolution*)els->user_data; - solution = sol->solution; - nsol = sol->ncoil; + FwdBemSolution* sol = els->user_data.get(); + nsol = sol->ncoil; + for (k = 0; k < nsol; k++) + grad[k] = sol->solution.row(k).dot(v0); } else { - solution = m->solution; - nsol = all_surfs ? m->nsol : m->surfs[0]->np; + nsol = all_surfs ? this->nsol : surfs[0]->np; + for (k = 0; k < nsol; k++) + grad[k] = solution.row(k).dot(v0); } - for (k = 0; k < nsol; k++) - grad[k] = mne_dot_vectors_40(solution[k],v0,m->nsol); } return; } //============================================================================================================= -void FwdBemModel::fwd_bem_pot_calc(float *rd, float *Q, FwdBemModel *m, FwdCoilSet *els, int all_surfs, float *pot) +void FwdBemModel::fwd_bem_pot_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet *els, int all_surfs, Eigen::VectorXf& pot) /* * Compute the potentials due to a current dipole */ @@ -1825,67 +1489,70 @@ void FwdBemModel::fwd_bem_pot_calc(float *rd, float *Q, FwdBemModel *m, FwdCoilS int ntri; int s,k,p,nsol; float mult; - float *v0; - float **solution; - float mri_rd[3],mri_Q[3]; - - if (!m->v0) - m->v0 = MALLOC_40(m->nsol,float); - v0 = m->v0; - - VEC_COPY_40(mri_rd,rd); - VEC_COPY_40(mri_Q,Q); - if (!m->head_mri_t.isEmpty()) { - FiffCoordTrans::apply_trans(mri_rd,m->head_mri_t,FIFFV_MOVE); - FiffCoordTrans::apply_trans(mri_Q,m->head_mri_t,FIFFV_NO_MOVE); - } - for (s = 0, p = 0; s < m->nsurf; s++) { - ntri = m->surfs[s]->ntri; - tri = m->surfs[s]->tris.data(); - mult = m->source_mult[s]; + Eigen::Vector3f mri_rd = rd; + Eigen::Vector3f mri_Q = Q; + + if (v0.size() == 0) + v0.resize(this->nsol); + float* v0p = v0.data(); + + if (!head_mri_t.isEmpty()) { + FiffCoordTrans::apply_trans(mri_rd.data(),head_mri_t,FIFFV_MOVE); + FiffCoordTrans::apply_trans(mri_Q.data(),head_mri_t,FIFFV_NO_MOVE); + } + for (s = 0, p = 0; s < nsurf; s++) { + ntri = surfs[s]->ntri; + tri = surfs[s]->tris.data(); + mult = source_mult[s]; for (k = 0; k < ntri; k++, tri++) - v0[p++] = mult*fwd_bem_inf_pot(mri_rd,mri_Q,tri->cent.data()); + v0p[p++] = mult*fwd_bem_inf_pot(mri_rd,mri_Q,tri->cent); } if (els) { - FwdBemSolution* sol = (FwdBemSolution*)els->user_data; - solution = sol->solution; - nsol = sol->ncoil; + FwdBemSolution* sol = els->user_data.get(); + nsol = sol->ncoil; + for (k = 0; k < nsol; k++) + pot[k] = sol->solution.row(k).dot(v0); } else { - solution = m->solution; - nsol = all_surfs ? m->nsol : m->surfs[0]->ntri; + nsol = all_surfs ? this->nsol : surfs[0]->ntri; + for (k = 0; k < nsol; k++) + pot[k] = solution.row(k).dot(v0); } - for (k = 0; k < nsol; k++) - pot[k] = mne_dot_vectors_40(solution[k],v0,m->nsol); return; } //============================================================================================================= -int FwdBemModel::fwd_bem_pot_els(float *rd, float *Q, FwdCoilSet *els, float *pot, void *client) /* The model */ +int FwdBemModel::fwd_bem_pot_els(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet *els, float *pot, void *client) /* The model */ /* * This version calculates the potential on all surfaces */ { FwdBemModel* m = (FwdBemModel*)client; - FwdBemSolution* sol = (FwdBemSolution*)els->user_data; + FwdBemSolution* sol = els->user_data.get(); if (!m) { printf("No BEM model specified to fwd_bem_pot_els"); return FAIL; } - if (!m->solution) { + if (m->solution.size() == 0) { printf("No solution available for fwd_bem_pot_els"); return FAIL; } - if (!sol || sol->ncoil != els->ncoil) { + if (!sol || sol->ncoil != els->ncoil()) { printf("No appropriate electrode-specific data available in fwd_bem_pot_coils"); return FAIL; } - if (m->bem_method == FWD_BEM_CONSTANT_COLL) - fwd_bem_pot_calc(rd,Q,m,els,FALSE,pot); - else if (m->bem_method == FWD_BEM_LINEAR_COLL) - fwd_bem_lin_pot_calc(rd,Q,m,els,FALSE,pot); + if (m->bem_method == FWD_BEM_CONSTANT_COLL) { + Eigen::VectorXf potVec(els->ncoil()); + m->fwd_bem_pot_calc(rd,Q,els,FALSE,potVec); + Eigen::Map(pot, els->ncoil()) = potVec; + } + else if (m->bem_method == FWD_BEM_LINEAR_COLL) { + Eigen::VectorXf potVec(els->ncoil()); + m->fwd_bem_lin_pot_calc(rd,Q,els,FALSE,potVec); + Eigen::Map(pot, els->ncoil()) = potVec; + } else { printf("Unknown BEM method : %d",m->bem_method); return FAIL; @@ -1895,35 +1562,51 @@ int FwdBemModel::fwd_bem_pot_els(float *rd, float *Q, FwdCoilSet *els, float *po //============================================================================================================= -int FwdBemModel::fwd_bem_pot_grad_els(float *rd, float *Q, FwdCoilSet *els, float *pot, float *xgrad, float *ygrad, float *zgrad, void *client) /* The model */ +int FwdBemModel::fwd_bem_pot_grad_els(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet *els, float *pot, float *xgrad, float *ygrad, float *zgrad, void *client) /* The model */ /* * This version calculates the potential on all surfaces */ { FwdBemModel* m = (FwdBemModel*)client; - FwdBemSolution* sol = (FwdBemSolution*)els->user_data; + FwdBemSolution* sol = els->user_data.get(); if (!m) { qCritical("No BEM model specified to fwd_bem_pot_els"); return FAIL; } - if (!m->solution) { + if (m->solution.size() == 0) { qCritical("No solution available for fwd_bem_pot_els"); return FAIL; } - if (!sol || sol->ncoil != els->ncoil) { + if (!sol || sol->ncoil != els->ncoil()) { qCritical("No appropriate electrode-specific data available in fwd_bem_pot_coils"); return FAIL; } if (m->bem_method == FWD_BEM_CONSTANT_COLL) { - if (pot) - fwd_bem_pot_calc(rd,Q,m,els,FALSE,pot); - fwd_bem_pot_grad_calc(rd,Q,m,els,FALSE,xgrad,ygrad,zgrad); + int n = els->ncoil(); + if (pot) { + Eigen::VectorXf potVec(n); + m->fwd_bem_pot_calc(rd,Q,els,FALSE,potVec); + Eigen::Map(pot, n) = potVec; + } + Eigen::VectorXf xg(n), yg(n), zg(n); + m->fwd_bem_pot_grad_calc(rd,Q,els,FALSE,xg,yg,zg); + Eigen::Map(xgrad, n) = xg; + Eigen::Map(ygrad, n) = yg; + Eigen::Map(zgrad, n) = zg; } else if (m->bem_method == FWD_BEM_LINEAR_COLL) { - if (pot) - fwd_bem_lin_pot_calc(rd,Q,m,els,FALSE,pot); - fwd_bem_lin_pot_grad_calc(rd,Q,m,els,FALSE,xgrad,ygrad,zgrad); + int n = els->ncoil(); + if (pot) { + Eigen::VectorXf potVec(n); + m->fwd_bem_lin_pot_calc(rd,Q,els,FALSE,potVec); + Eigen::Map(pot, n) = potVec; + } + Eigen::VectorXf xg(n), yg(n), zg(n); + m->fwd_bem_lin_pot_grad_calc(rd,Q,els,FALSE,xg,yg,zg); + Eigen::Map(xgrad, n) = xg; + Eigen::Map(ygrad, n) = yg; + Eigen::Map(zgrad, n) = zg; } else { qCritical("Unknown BEM method : %d",m->bem_method); @@ -1936,54 +1619,49 @@ int FwdBemModel::fwd_bem_pot_grad_els(float *rd, float *Q, FwdCoilSet *els, floa #define ARSINH(x) log((x) + sqrt(1.0+(x)*(x))) -void FwdBemModel::calc_f(double *xx, double *yy, double *f0, double *fx, double *fy) /* The weights in the linear approximation */ +void FwdBemModel::calc_f(const Eigen::Vector3d& xx, const Eigen::Vector3d& yy, Eigen::Vector3d& f0, Eigen::Vector3d& fx, Eigen::Vector3d& fy) { - double det = -xx[Y_40]*yy[X_40] + xx[Z_40]*yy[X_40] + - xx[X_40]*yy[Y_40] - xx[Z_40]*yy[Y_40] - xx[X_40]*yy[Z_40] + xx[Y_40]*yy[Z_40]; - int k; + double det = -xx[1]*yy[0] + xx[2]*yy[0] + + xx[0]*yy[1] - xx[2]*yy[1] - xx[0]*yy[2] + xx[1]*yy[2]; - f0[X_40] = -xx[Z_40]*yy[Y_40] + xx[Y_40]*yy[Z_40]; - f0[Y_40] = xx[Z_40]*yy[X_40] - xx[X_40]*yy[Z_40]; - f0[Z_40] = -xx[Y_40]*yy[X_40] + xx[X_40]*yy[Y_40]; + f0[0] = -xx[2]*yy[1] + xx[1]*yy[2]; + f0[1] = xx[2]*yy[0] - xx[0]*yy[2]; + f0[2] = -xx[1]*yy[0] + xx[0]*yy[1]; - fx[X_40] = yy[Y_40] - yy[Z_40]; - fx[Y_40] = -yy[X_40] + yy[Z_40]; - fx[Z_40] = yy[X_40] - yy[Y_40]; + fx[0] = yy[1] - yy[2]; + fx[1] = -yy[0] + yy[2]; + fx[2] = yy[0] - yy[1]; - fy[X_40] = -xx[Y_40] + xx[Z_40]; - fy[Y_40] = xx[X_40] - xx[Z_40]; - fy[Z_40] = -xx[X_40] + xx[Y_40]; + fy[0] = -xx[1] + xx[2]; + fy[1] = xx[0] - xx[2]; + fy[2] = -xx[0] + xx[1]; - for (k = 0; k < 3; k++) { - f0[k] = f0[k]/det; - fx[k] = fx[k]/det; - fy[k] = fy[k]/det; - } + f0 /= det; + fx /= det; + fy /= det; } //============================================================================================================= -void FwdBemModel::calc_magic(double u, double z, double A, double B, double *beta, double *D) -/* - * Calculate Urankar's magic numbers - */ +void FwdBemModel::calc_magic(double u, double z, double A, double B, Eigen::Vector3d& beta, double& D) { double B2 = 1.0 + B*B; double ABu = A + B*u; - *D = sqrt(u*u + z*z + ABu*ABu); + D = sqrt(u*u + z*z + ABu*ABu); beta[0] = ABu/sqrt(u*u + z*z); beta[1] = (A*B + B2*u)/sqrt(A*A + B2*z*z); - beta[2] = (B*z*z - A*u)/(z*(*D)); + beta[2] = (B*z*z - A*u)/(z*D); } //============================================================================================================= -void FwdBemModel::field_integrals(float *from, MNETriangle* to, double *I1p, double *T, double *S1, double *S2, double *f0, double *fx, double *fy) +void FwdBemModel::field_integrals(const Eigen::Vector3f& from, MNETriangle& to, double& I1p, Eigen::Vector2d& T, Eigen::Vector2d& S1, Eigen::Vector2d& S2, Eigen::Vector3d& f0, Eigen::Vector3d& fx, Eigen::Vector3d& fy) { double y1[3],y2[3],y3[3]; double xx[4],yy[4]; double A,B,z,dx; - double beta[3],I1,Tx,Ty,Txx,Tyy,Sxx,mult; + Eigen::Vector3d beta; + double I1,Tx,Ty,Txx,Tyy,Sxx,mult; double S1x,S1y,S2x,S2y; double D1,B2; int k; @@ -1993,27 +1671,27 @@ void FwdBemModel::field_integrals(float *from, MNETriangle* to, double *I1p, dou * 1. Move origin to viewpoint... * */ - VEC_DIFF_40(from,to->r1,y1); - VEC_DIFF_40(from,to->r2,y2); - VEC_DIFF_40(from,to->r3,y3); + vec_diff(from,to.r1,y1); + vec_diff(from,to.r2,y2); + vec_diff(from,to.r3,y3); /* * 2. Calculate local xy coordinates... */ - xx[0] = VEC_DOT_40(y1,to->ex); - xx[1] = VEC_DOT_40(y2,to->ex); - xx[2] = VEC_DOT_40(y3,to->ex); + xx[0] = vec_dot(y1,to.ex); + xx[1] = vec_dot(y2,to.ex); + xx[2] = vec_dot(y3,to.ex); xx[3] = xx[0]; - yy[0] = VEC_DOT_40(y1,to->ey); - yy[1] = VEC_DOT_40(y2,to->ey); - yy[2] = VEC_DOT_40(y3,to->ey); + yy[0] = vec_dot(y1,to.ey); + yy[1] = vec_dot(y2,to.ey); + yy[2] = vec_dot(y3,to.ey); yy[3] = yy[0]; - calc_f (xx,yy,f0,fx,fy); + calc_f(Eigen::Map(xx), Eigen::Map(yy), f0, fx, fy); /* * 3. Distance of the plane from origin... */ - z = VEC_DOT_40(y1,to->nn); + z = vec_dot(y1,to.nn); /* * Put together the line integral... * We use the convention where the local y-axis @@ -2036,7 +1714,7 @@ void FwdBemModel::field_integrals(float *from, MNETriangle* to, double *I1p, dou /* * Upper limit */ - calc_magic (xx[k+1],z,A,B,beta,&D1); + calc_magic(xx[k+1],z,A,B,beta,D1); I1 = I1 - xx[k+1]*ARSINH(beta[0]) - (A/sqrt(1.0+B*B))*ARSINH(beta[1]) - z*atan(beta[2]); Txx = ARSINH(beta[1])/sqrt(B2); @@ -2050,7 +1728,7 @@ void FwdBemModel::field_integrals(float *from, MNETriangle* to, double *I1p, dou /* * Lower limit */ - calc_magic (xx[k],z,A,B,beta,&D1); + calc_magic(xx[k],z,A,B,beta,D1); I1 = I1 + xx[k]*ARSINH(beta[0]) + (A/sqrt(1.0+B*B))*ARSINH(beta[1]) + z*atan(beta[2]); Txx = ARSINH(beta[1])/sqrt(B2); @@ -2081,23 +1759,18 @@ void FwdBemModel::field_integrals(float *from, MNETriangle* to, double *I1p, dou /* * Set return values */ - *I1p = I1; - T[X_40] = Tx; - T[Y_40] = Ty; - S1[X_40] = S1x; - S1[Y_40] = S1y; - S2[X_40] = S2x; - S2[Y_40] = -S1x; - return; + I1p = I1; + T[0] = Tx; + T[1] = Ty; + S1[0] = S1x; + S1[1] = S1y; + S2[0] = S2x; + S2[1] = -S1x; } //============================================================================================================= -double FwdBemModel::one_field_coeff(float *dest, float *normal, MNETriangle* tri) -/* - * Compute the integral over one triangle. - * This looks magical but it is not. - */ +double FwdBemModel::one_field_coeff(const Eigen::Vector3f& dest, const Eigen::Vector3f& normal, MNETriangle& tri) { double *yy[4]; double y1[3],y2[3],y3[3]; @@ -2110,11 +1783,11 @@ double FwdBemModel::one_field_coeff(float *dest, float *normal, MNETriangle* tri yy[1] = y2; yy[2] = y3; yy[3] = y1; - VEC_DIFF_40(dest,tri->r1,y1); - VEC_DIFF_40(dest,tri->r2,y2); - VEC_DIFF_40(dest,tri->r3,y3); + vec_diff(dest,tri.r1,y1); + vec_diff(dest,tri.r2,y2); + vec_diff(dest,tri.r3,y3); for (j = 0; j < 3; j++) - beta[j] = calc_beta(yy[j],yy[j+1]); + beta[j] = calc_beta(Eigen::Map(yy[j]),Eigen::Map(yy[j+1])); bbeta[0] = beta[2] - beta[0]; bbeta[1] = beta[0] - beta[1]; bbeta[2] = beta[1] - beta[2]; @@ -2124,12 +1797,12 @@ double FwdBemModel::one_field_coeff(float *dest, float *normal, MNETriangle* tri for (j = 0; j < 3; j++) for (k = 0; k < 3; k++) coeff[k] = coeff[k] + yy[j][k]*bbeta[j]; - return (VEC_DOT_40(coeff,normal)); + return (vec_dot(coeff,normal)); } //============================================================================================================= -float **FwdBemModel::fwd_bem_field_coeff(FwdBemModel *m, FwdCoilSet *coils) /* Gradiometer coil positions */ +Eigen::MatrixXf FwdBemModel::fwd_bem_field_coeff(FwdCoilSet *coils) /* Gradiometer coil positions */ /* * Compute the weighting factors to obtain the magnetic field */ @@ -2139,59 +1812,58 @@ float **FwdBemModel::fwd_bem_field_coeff(FwdBemModel *m, FwdCoilSet *coils) /* G FwdCoil* coil; FwdCoilSet* tcoils = NULL; int ntri; - float **coeff = NULL; int j,k,p,s,off; double res; double mult; - if (m->solution == NULL) { + if (solution.size() == 0) { printf("Solution matrix missing in fwd_bem_field_coeff"); - return NULL; + return Eigen::MatrixXf(); } - if (m->bem_method != FWD_BEM_CONSTANT_COLL) { + if (bem_method != FWD_BEM_CONSTANT_COLL) { printf("BEM method should be constant collocation for fwd_bem_field_coeff"); - return NULL; + return Eigen::MatrixXf(); } if (coils->coord_frame != FIFFV_COORD_MRI) { if (coils->coord_frame == FIFFV_COORD_HEAD) { - if (m->head_mri_t.isEmpty()) { + if (head_mri_t.isEmpty()) { printf("head -> mri coordinate transform missing in fwd_bem_field_coeff"); - return NULL; + return Eigen::MatrixXf(); } else { if (!coils) { qWarning("No coils to duplicate"); - return NULL; + return Eigen::MatrixXf(); } /* * Make a transformed duplicate */ - if ((tcoils = coils->dup_coil_set(m->head_mri_t)) == NULL) - return NULL; + if ((tcoils = coils->dup_coil_set(head_mri_t)) == NULL) + return Eigen::MatrixXf(); coils = tcoils; } } else { printf("Incompatible coil coordinate frame %d for fwd_bem_field_coeff",coils->coord_frame); - return NULL; + return Eigen::MatrixXf(); } } - ntri = m->nsol; - coeff = ALLOC_CMATRIX_40(coils->ncoil,ntri); + ntri = nsol; + Eigen::MatrixXf coeff = Eigen::MatrixXf::Zero(coils->ncoil(), ntri); - for (s = 0, off = 0; s < m->nsurf; s++) { - surf = m->surfs[s]; + for (s = 0, off = 0; s < nsurf; s++) { + surf = surfs[s].get(); ntri = surf->ntri; tri = surf->tris.data(); - mult = m->field_mult[s]; + mult = field_mult[s]; for (k = 0; k < ntri; k++,tri++) { - for (j = 0; j < coils->ncoil; j++) { - coil = coils->coils[j]; + for (j = 0; j < coils->ncoil(); j++) { + coil = coils->coils[j].get(); res = 0.0; for (p = 0; p < coil->np; p++) - res = res + coil->w[p]*one_field_coeff(coil->rmag[p],coil->cosmag[p],tri); - coeff[j][k+off] = mult*res; + res = res + coil->w[p]*one_field_coeff(Eigen::Map(&coil->rmag(p, 0)),Eigen::Map(&coil->cosmag(p, 0)),*tri); + coeff(j,k+off) = mult*res; } } off = off + ntri; @@ -2202,23 +1874,18 @@ float **FwdBemModel::fwd_bem_field_coeff(FwdBemModel *m, FwdCoilSet *coils) /* G //============================================================================================================= -double FwdBemModel::calc_gamma(double *rk, double *rk1) +double FwdBemModel::calc_gamma(const Eigen::Vector3d& rk, const Eigen::Vector3d& rk1) { - double rkk1[3]; - double size; - double res; - - VEC_DIFF_40(rk,rk1,rkk1); - size = VEC_LEN_40(rkk1); + Eigen::Vector3d rkk1 = rk1 - rk; + double size = rkk1.norm(); - res = log((VEC_LEN_40(rk1)*size + VEC_DOT_40(rk1,rkk1))/ - (VEC_LEN_40(rk)*size + VEC_DOT_40(rk,rkk1)))/size; - return (res); + return log((rk1.norm() * size + rk1.dot(rkk1)) / + (rk.norm() * size + rk.dot(rkk1))) / size; } //============================================================================================================= -void FwdBemModel::fwd_bem_one_lin_field_coeff_ferg(float *dest, float *dir, MNETriangle* tri, double *res) /* The results */ +void FwdBemModel::fwd_bem_one_lin_field_coeff_ferg(const Eigen::Vector3f& dest, const Eigen::Vector3f& dir, MNETriangle& tri, Eigen::Vector3d& res) { double c[3]; /* Component of dest vector normal to * the triangle plane */ @@ -2236,116 +1903,109 @@ void FwdBemModel::fwd_bem_one_lin_field_coeff_ferg(float *dest, float *dir, MNET yy[2] = y3; cc[2] = c3; yy[3] = y1; cc[3] = c1; - VEC_DIFF_40(tri->r2,tri->r3,rjk[0]); - VEC_DIFF_40(tri->r3,tri->r1,rjk[1]); - VEC_DIFF_40(tri->r1,tri->r2,rjk[2]); + vec_diff(tri.r2,tri.r3,rjk[0]); + vec_diff(tri.r3,tri.r1,rjk[1]); + vec_diff(tri.r1,tri.r2,rjk[2]); for (k = 0; k < 3; k++) { - y1[k] = tri->r1[k] - dest[k]; - y2[k] = tri->r2[k] - dest[k]; - y3[k] = tri->r3[k] - dest[k]; + y1[k] = tri.r1[k] - dest[k]; + y2[k] = tri.r2[k] - dest[k]; + y3[k] = tri.r3[k] - dest[k]; } - clen = VEC_DOT_40(y1,tri->nn); + clen = vec_dot(y1,tri.nn); for (k = 0; k < 3; k++) { - c[k] = clen*tri->nn[k]; + c[k] = clen*tri.nn[k]; A[k] = dest[k] + c[k]; - c1[k] = tri->r1[k] - A[k]; - c2[k] = tri->r2[k] - A[k]; - c3[k] = tri->r3[k] - A[k]; + c1[k] = tri.r1[k] - A[k]; + c2[k] = tri.r2[k] - A[k]; + c3[k] = tri.r3[k] - A[k]; } /* * beta and gamma... */ for (sum = 0.0, k = 0; k < 3; k++) { - CROSS_PRODUCT_40(cc[k],cc[k+1],cross); - beta = VEC_DOT_40(cross,tri->nn); - gamma = calc_gamma (yy[k],yy[k+1]); + cross_product(cc[k],cc[k+1],cross); + beta = vec_dot(cross,tri.nn); + gamma = calc_gamma (Eigen::Map(yy[k]),Eigen::Map(yy[k+1])); sum = sum + beta*gamma; } /* * Solid angle... */ - CROSS_PRODUCT_40(y1,y2,cross); - triple = VEC_DOT_40(cross,y3); + cross_product(y1,y2,cross); + triple = vec_dot(cross,y3); - l1 = VEC_LEN_40(y1); - l2 = VEC_LEN_40(y2); - l3 = VEC_LEN_40(y3); + l1 = vec_len(y1); + l2 = vec_len(y2); + l3 = vec_len(y3); solid = 2.0*atan2(triple, (l1*l2*l3+ - VEC_DOT_40(y1,y2)*l3+ - VEC_DOT_40(y1,y3)*l2+ - VEC_DOT_40(y2,y3)*l1)); + vec_dot(y1,y2)*l3+ + vec_dot(y1,y3)*l2+ + vec_dot(y2,y3)*l1)); /* * Now we are ready to assemble it all together */ - common = (sum-clen*solid)/(2.0*tri->area); + common = (sum-clen*solid)/(2.0*tri.area); for (k = 0; k < 3; k++) - res[k] = -VEC_DOT_40(rjk[k],dir)*common; + res[k] = -vec_dot(rjk[k],dir)*common; return; } //============================================================================================================= -void FwdBemModel::fwd_bem_one_lin_field_coeff_uran(float *dest, float *dir, MNETriangle* tri, double *res) /* The results */ +void FwdBemModel::fwd_bem_one_lin_field_coeff_uran(const Eigen::Vector3f& dest, const Eigen::Vector3f& dir_in, MNETriangle& tri, Eigen::Vector3d& res) { - double I1,T[2],S1[2],S2[2]; - double f0[3],fx[3],fy[3]; + double I1; + Eigen::Vector2d T, S1, S2; + Eigen::Vector3d f0, fx, fy; double res_x,res_y; double x_fac,y_fac; int k; - double len; /* * Compute the component integrals */ - field_integrals (dest,tri,&I1,T,S1,S2,f0,fx,fy); + field_integrals(dest,tri,I1,T,S1,S2,f0,fx,fy); /* * Compute the coefficient for each node... */ - len = VEC_LEN_40(dir); - dir[X_40] = dir[X_40]/len; - dir[Y_40] = dir[Y_40]/len; - dir[Z_40] = dir[Z_40]/len; + Eigen::Vector3f dir = dir_in.normalized(); - x_fac = -VEC_DOT_40(dir,tri->ex); - y_fac = -VEC_DOT_40(dir,tri->ey); + x_fac = -vec_dot(dir,tri.ex); + y_fac = -vec_dot(dir,tri.ey); for (k = 0; k < 3; k++) { - res_x = f0[k]*T[X_40] + fx[k]*S1[X_40] + fy[k]*S2[X_40] + fy[k]*I1; - res_y = f0[k]*T[Y_40] + fx[k]*S1[Y_40] + fy[k]*S2[Y_40] - fx[k]*I1; + res_x = f0[k]*T[0] + fx[k]*S1[0] + fy[k]*S2[0] + fy[k]*I1; + res_y = f0[k]*T[1] + fx[k]*S1[1] + fy[k]*S2[1] - fx[k]*I1; res[k] = x_fac*res_x + y_fac*res_y; } - return; } //============================================================================================================= -void FwdBemModel::fwd_bem_one_lin_field_coeff_simple(float *dest, float *normal, MNETriangle* source, double *res) /* The result for each triangle node */ -/* - * Simple version... - */ +void FwdBemModel::fwd_bem_one_lin_field_coeff_simple(const Eigen::Vector3f& dest, const Eigen::Vector3f& normal, MNETriangle& source, Eigen::Vector3d& res) { float diff[3]; float vec_result[3]; float dl; int k; - const float *rr[3]; + const Eigen::Vector3f* rr[3]; - rr[0] = source->r1; - rr[1] = source->r2; - rr[2] = source->r3; + rr[0] = &source.r1; + rr[1] = &source.r2; + rr[2] = &source.r3; for (k = 0; k < 3; k++) { - VEC_DIFF_40(rr[k],dest,diff); - dl = VEC_DOT_40(diff,diff); - CROSS_PRODUCT_40(diff,source->nn,vec_result); - res[k] = source->area*VEC_DOT_40(vec_result,normal)/(3.0*dl*sqrt(dl)); + vec_diff(*rr[k],dest,diff); + dl = vec_dot(diff,diff); + cross_product(diff,source.nn,vec_result); + res[k] = source.area*vec_dot(vec_result,normal)/(3.0*dl*sqrt(dl)); } return; } //============================================================================================================= -float **FwdBemModel::fwd_bem_lin_field_coeff(FwdBemModel *m, FwdCoilSet *coils, int method) /* Which integration formula to use */ +Eigen::MatrixXf FwdBemModel::fwd_bem_lin_field_coeff(FwdCoilSet *coils, int method) /* Which integration formula to use */ /* * Compute the weighting factors to obtain the magnetic field * in the linear potential approximation @@ -2356,42 +2016,41 @@ float **FwdBemModel::fwd_bem_lin_field_coeff(FwdBemModel *m, FwdCoilSet *coils, FwdCoil* coil; FwdCoilSet* tcoils = NULL; int ntri; - float **coeff = NULL; int j,k,p,pp,off,s; - double res[3],one[3]; + Eigen::Vector3d res, one; float mult; linFieldIntFunc func; - if (m->solution == NULL) { + if (solution.size() == 0) { printf("Solution matrix missing in fwd_bem_lin_field_coeff"); - return NULL; + return Eigen::MatrixXf(); } - if (m->bem_method != FWD_BEM_LINEAR_COLL) { + if (bem_method != FWD_BEM_LINEAR_COLL) { printf("BEM method should be linear collocation for fwd_bem_lin_field_coeff"); - return NULL; + return Eigen::MatrixXf(); } if (coils->coord_frame != FIFFV_COORD_MRI) { if (coils->coord_frame == FIFFV_COORD_HEAD) { - if (m->head_mri_t.isEmpty()) { + if (head_mri_t.isEmpty()) { printf("head -> mri coordinate transform missing in fwd_bem_lin_field_coeff"); - return NULL; + return Eigen::MatrixXf(); } else { if (!coils) { qWarning("No coils to duplicate"); - return NULL; + return Eigen::MatrixXf(); } /* * Make a transformed duplicate */ - if ((tcoils = coils->dup_coil_set(m->head_mri_t)) == NULL) - return NULL; + if ((tcoils = coils->dup_coil_set(head_mri_t)) == NULL) + return Eigen::MatrixXf(); coils = tcoils; } } else { printf("Incompatible coil coordinate frame %d for fwd_bem_field_coeff",coils->coord_frame); - return NULL; + return Eigen::MatrixXf(); } } if (method == FWD_BEM_LIN_FIELD_FERGUSON) @@ -2401,38 +2060,33 @@ float **FwdBemModel::fwd_bem_lin_field_coeff(FwdBemModel *m, FwdCoilSet *coils, else func = fwd_bem_one_lin_field_coeff_simple; - coeff = ALLOC_CMATRIX_40(coils->ncoil,m->nsol); - for (k = 0; k < m->nsol; k++) - for (j = 0; j < coils->ncoil; j++) - coeff[j][k] = 0.0; + Eigen::MatrixXf coeff = Eigen::MatrixXf::Zero(coils->ncoil(), nsol); /* * Process each of the surfaces */ - for (s = 0, off = 0; s < m->nsurf; s++) { - surf = m->surfs[s]; + for (s = 0, off = 0; s < nsurf; s++) { + surf = surfs[s].get(); ntri = surf->ntri; tri = surf->tris.data(); - mult = m->field_mult[s]; + mult = field_mult[s]; for (k = 0; k < ntri; k++,tri++) { - for (j = 0; j < coils->ncoil; j++) { - coil = coils->coils[j]; - for (pp = 0; pp < 3; pp++) - res[pp] = 0; + for (j = 0; j < coils->ncoil(); j++) { + coil = coils->coils[j].get(); + res.setZero(); /* * Accumulate the coefficients for each triangle node... */ for (p = 0; p < coil->np; p++) { - func(coil->rmag[p],coil->cosmag[p],tri,one); - for (pp = 0; pp < 3; pp++) - res[pp] = res[pp] + coil->w[p]*one[pp]; + func(Eigen::Map(&coil->rmag(p, 0)),Eigen::Map(&coil->cosmag(p, 0)),*tri,one); + res += coil->w[p] * one; } /* * Add these to the corresponding coefficient matrix * elements... */ for (pp = 0; pp < 3; pp++) - coeff[j][tri->vert[pp]+off] = coeff[j][tri->vert[pp]+off] + mult*res[pp]; + coeff(j,tri->vert[pp]+off) = coeff(j,tri->vert[pp]+off) + mult*res[pp]; } } off = off + surf->np; @@ -2441,256 +2095,250 @@ float **FwdBemModel::fwd_bem_lin_field_coeff(FwdBemModel *m, FwdCoilSet *coils, * Discard the duplicate */ delete tcoils; - return (coeff); + return coeff; } //============================================================================================================= -int FwdBemModel::fwd_bem_specify_coils(FwdBemModel *m, FwdCoilSet *coils) +int FwdBemModel::fwd_bem_specify_coils(FwdCoilSet *coils) /* * Set up for computing the solution at a set of coils */ { - float **sol = NULL; - FwdBemSolution* csol; + Eigen::MatrixXf sol; + FwdBemSolution* csol = nullptr; - if (!m) { - printf("Model missing in fwd_bem_specify_coils"); - goto bad; - } - if (!m->solution) { + if (solution.size() == 0) { printf("Solution not computed in fwd_bem_specify_coils"); goto bad; } if(coils) coils->fwd_free_coil_set_user_data(); - if (!coils || coils->ncoil == 0) + if (!coils || coils->ncoil() == 0) return OK; - if (m->bem_method == FWD_BEM_CONSTANT_COLL) - sol = fwd_bem_field_coeff(m,coils); - else if (m->bem_method == FWD_BEM_LINEAR_COLL) - sol = fwd_bem_lin_field_coeff(m,coils,FWD_BEM_LIN_FIELD_SIMPLE); + if (bem_method == FWD_BEM_CONSTANT_COLL) + sol = fwd_bem_field_coeff(coils); + else if (bem_method == FWD_BEM_LINEAR_COLL) + sol = fwd_bem_lin_field_coeff(coils,FWD_BEM_LIN_FIELD_SIMPLE); else { - printf("Unknown BEM method in fwd_bem_specify_coils : %d",m->bem_method); + printf("Unknown BEM method in fwd_bem_specify_coils : %d",bem_method); goto bad; } - coils->user_data = csol = new FwdBemSolution(); - coils->user_data_free = FwdBemSolution::fwd_bem_free_coil_solution; + if (sol.size() == 0) + goto bad; + coils->user_data = std::make_unique(); + csol = coils->user_data.get(); - csol->ncoil = coils->ncoil; - csol->np = m->nsol; - csol->solution = mne_mat_mat_mult_40(sol, - m->solution, - coils->ncoil, - m->nsol, - m->nsol);//TODO: Suspicion, that this is slow - use Eigen + csol->ncoil = coils->ncoil(); + csol->np = nsol; + csol->solution = sol * solution; - FREE_CMATRIX_40(sol); return OK; bad : { - FREE_CMATRIX_40(sol); return FAIL; - } } //============================================================================================================= -void FwdBemModel::fwd_bem_lin_field_calc(float *rd, float *Q, FwdCoilSet *coils, FwdBemModel *m, float *B) +void FwdBemModel::fwd_bem_lin_field_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet& coils, Eigen::VectorXf& B) /* * Calculate the magnetic field in a set of coils */ { - float *v0; int s,k,p,np; FwdCoil* coil; float mult; - float my_rd[3],my_Q[3]; - FwdBemSolution* sol = (FwdBemSolution*)coils->user_data; + Eigen::Vector3f my_rd = rd; + Eigen::Vector3f my_Q = Q; + FwdBemSolution* sol = coils.user_data.get(); /* * Infinite-medium potentials */ - if (!m->v0) - m->v0 = MALLOC_40(m->nsol,float); - v0 = m->v0; + if (v0.size() == 0) + v0.resize(nsol); + float* v0p = v0.data(); /* * The dipole location and orientation must be transformed */ - VEC_COPY_40(my_rd,rd); - VEC_COPY_40(my_Q,Q); - if (!m->head_mri_t.isEmpty()) { - FiffCoordTrans::apply_trans(my_rd,m->head_mri_t,FIFFV_MOVE); - FiffCoordTrans::apply_trans(my_Q,m->head_mri_t,FIFFV_NO_MOVE); + if (!head_mri_t.isEmpty()) { + FiffCoordTrans::apply_trans(my_rd.data(),head_mri_t,FIFFV_MOVE); + FiffCoordTrans::apply_trans(my_Q.data(),head_mri_t,FIFFV_NO_MOVE); } /* * Compute the inifinite-medium potentials at the vertices */ - for (s = 0, p = 0; s < m->nsurf; s++) { - np = m->surfs[s]->np; - mult = m->source_mult[s]; + for (s = 0, p = 0; s < nsurf; s++) { + np = surfs[s]->np; + mult = source_mult[s]; for (k = 0; k < np; k++) - v0[p++] = mult*fwd_bem_inf_pot(my_rd,my_Q,&m->surfs[s]->rr(k,0)); + v0p[p++] = mult*fwd_bem_inf_pot(my_rd,my_Q,Eigen::Map(&surfs[s]->rr(k,0))); } /* * Primary current contribution * (can be calculated in the coil/dipole coordinates) */ - for (k = 0; k < coils->ncoil; k++) { - coil = coils->coils[k]; + for (k = 0; k < coils.ncoil(); k++) { + coil = coils.coils[k].get(); B[k] = 0.0; for (p = 0; p < coil->np; p++) - B[k] = B[k] + coil->w[p]*fwd_bem_inf_field(rd,Q,coil->rmag[p],coil->cosmag[p]); + B[k] = B[k] + coil->w[p]*fwd_bem_inf_field( + rd, + Q, + Eigen::Map(&coil->rmag(p, 0)), + Eigen::Map(&coil->cosmag(p, 0))); } /* * Volume current contribution */ - for (k = 0; k < coils->ncoil; k++) - B[k] = B[k] + mne_dot_vectors_40(sol->solution[k],v0,m->nsol); + for (k = 0; k < coils.ncoil(); k++) + B[k] = B[k] + sol->solution.row(k).dot(v0); /* * Scale correctly */ - for (k = 0; k < coils->ncoil; k++) + for (k = 0; k < coils.ncoil(); k++) B[k] = MAG_FACTOR*B[k]; return; } //============================================================================================================= -void FwdBemModel::fwd_bem_field_calc(float *rd, float *Q, FwdCoilSet *coils, FwdBemModel *m, float *B) +void FwdBemModel::fwd_bem_field_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet& coils, Eigen::VectorXf& B) /* * Calculate the magnetic field in a set of coils */ { - float *v0; int s,k,p,ntri; FwdCoil* coil; MNETriangle* tri; float mult; - float my_rd[3],my_Q[3]; - FwdBemSolution* sol = (FwdBemSolution*)coils->user_data; + Eigen::Vector3f my_rd = rd; + Eigen::Vector3f my_Q = Q; + FwdBemSolution* sol = coils.user_data.get(); /* * Infinite-medium potentials */ - if (!m->v0) - m->v0 = MALLOC_40(m->nsol,float); - v0 = m->v0; + if (v0.size() == 0) + v0.resize(nsol); + float* v0p = v0.data(); /* * The dipole location and orientation must be transformed */ - VEC_COPY_40(my_rd,rd); - VEC_COPY_40(my_Q,Q); - if (!m->head_mri_t.isEmpty()) { - FiffCoordTrans::apply_trans(my_rd,m->head_mri_t,FIFFV_MOVE); - FiffCoordTrans::apply_trans(my_Q,m->head_mri_t,FIFFV_NO_MOVE); + if (!head_mri_t.isEmpty()) { + FiffCoordTrans::apply_trans(my_rd.data(),head_mri_t,FIFFV_MOVE); + FiffCoordTrans::apply_trans(my_Q.data(),head_mri_t,FIFFV_NO_MOVE); } /* * Compute the inifinite-medium potentials at the centers of the triangles */ - for (s = 0, p = 0; s < m->nsurf; s++) { - ntri = m->surfs[s]->ntri; - tri = m->surfs[s]->tris.data(); - mult = m->source_mult[s]; + for (s = 0, p = 0; s < nsurf; s++) { + ntri = surfs[s]->ntri; + tri = surfs[s]->tris.data(); + mult = source_mult[s]; for (k = 0; k < ntri; k++, tri++) - v0[p++] = mult*fwd_bem_inf_pot(my_rd,my_Q,tri->cent.data()); + v0p[p++] = mult*fwd_bem_inf_pot(my_rd,my_Q,tri->cent); } /* * Primary current contribution * (can be calculated in the coil/dipole coordinates) */ - for (k = 0; k < coils->ncoil; k++) { - coil = coils->coils[k]; + for (k = 0; k < coils.ncoil(); k++) { + coil = coils.coils[k].get(); B[k] = 0.0; for (p = 0; p < coil->np; p++) - B[k] = B[k] + coil->w[p]*fwd_bem_inf_field(rd,Q,coil->rmag[p],coil->cosmag[p]); + B[k] = B[k] + coil->w[p]*fwd_bem_inf_field( + rd, + Q, + Eigen::Map(&coil->rmag(p, 0)), + Eigen::Map(&coil->cosmag(p, 0))); } /* * Volume current contribution */ - for (k = 0; k < coils->ncoil; k++) - B[k] = B[k] + mne_dot_vectors_40(sol->solution[k],v0,m->nsol); + for (k = 0; k < coils.ncoil(); k++) + B[k] = B[k] + sol->solution.row(k).dot(v0); /* * Scale correctly */ - for (k = 0; k < coils->ncoil; k++) + for (k = 0; k < coils.ncoil(); k++) B[k] = MAG_FACTOR*B[k]; return; } //============================================================================================================= -void FwdBemModel::fwd_bem_field_grad_calc(float *rd, float *Q, FwdCoilSet* coils, FwdBemModel* m, float *xgrad, float *ygrad, float *zgrad) +void FwdBemModel::fwd_bem_field_grad_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet& coils, Eigen::VectorXf& xgrad, Eigen::VectorXf& ygrad, Eigen::VectorXf& zgrad) /* * Calculate the magnetic field in a set of coils */ { - FwdBemSolution* sol = (FwdBemSolution*)coils->user_data; - float *v0; + FwdBemSolution* sol = coils.user_data.get(); int s,k,p,ntri,pp; FwdCoil* coil; MNETriangle* tri; float mult; - float *grads[3],ee[3],mri_ee[3],mri_rd[3],mri_Q[3]; - float *grad; - - grads[0] = xgrad; - grads[1] = ygrad; - grads[2] = zgrad; + Eigen::VectorXf* grads[] = {&xgrad, &ygrad, &zgrad}; + Eigen::Vector3f ee, mri_ee; + Eigen::Vector3f mri_rd = rd; + Eigen::Vector3f mri_Q = Q; /* * Infinite-medium potentials */ - if (!m->v0) - m->v0 = MALLOC_40(m->nsol,float); - v0 = m->v0; + if (v0.size() == 0) + v0.resize(nsol); + float* v0p = v0.data(); /* * The dipole location and orientation must be transformed */ - VEC_COPY_40(mri_rd,rd); - VEC_COPY_40(mri_Q,Q); - if (!m->head_mri_t.isEmpty()) { - FiffCoordTrans::apply_trans(mri_rd,m->head_mri_t,FIFFV_MOVE); - FiffCoordTrans::apply_trans(mri_Q,m->head_mri_t,FIFFV_NO_MOVE); - } - for (pp = 0; pp < 3; pp++) { - grad = grads[pp]; + if (!head_mri_t.isEmpty()) { + FiffCoordTrans::apply_trans(mri_rd.data(),head_mri_t,FIFFV_MOVE); + FiffCoordTrans::apply_trans(mri_Q.data(),head_mri_t,FIFFV_NO_MOVE); + } + for (pp = X; pp <= Z; pp++) { + Eigen::VectorXf& grad = *grads[pp]; /* * Select the correct gradient component */ - for (p = 0; p < 3; p++) - ee[p] = p == pp ? 1.0 : 0.0; - VEC_COPY_40(mri_ee,ee); - if (!m->head_mri_t.isEmpty()) - FiffCoordTrans::apply_trans(mri_ee,m->head_mri_t,FIFFV_NO_MOVE); + ee = Eigen::Vector3f::Unit(pp); + mri_ee = ee; + if (!head_mri_t.isEmpty()) + FiffCoordTrans::apply_trans(mri_ee.data(),head_mri_t,FIFFV_NO_MOVE); /* * Compute the inifinite-medium potential derivatives at the centers of the triangles */ - for (s = 0, p = 0; s < m->nsurf; s++) { - ntri = m->surfs[s]->ntri; - tri = m->surfs[s]->tris.data(); - mult = m->source_mult[s]; + for (s = 0, p = 0; s < nsurf; s++) { + ntri = surfs[s]->ntri; + tri = surfs[s]->tris.data(); + mult = source_mult[s]; for (k = 0; k < ntri; k++, tri++) { - v0[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,tri->cent.data(),mri_ee); + v0p[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,tri->cent,mri_ee); } } /* * Primary current contribution * (can be calculated in the coil/dipole coordinates) */ - for (k = 0; k < coils->ncoil; k++) { - coil = coils->coils[k]; + for (k = 0; k < coils.ncoil(); k++) { + coil = coils.coils[k].get(); grad[k] = 0.0; for (p = 0; p < coil->np; p++) - grad[k] = grad[k] + coil->w[p]*fwd_bem_inf_field_der(rd,Q,coil->rmag[p],coil->cosmag[p],ee); + grad[k] = grad[k] + coil->w[p]*fwd_bem_inf_field_der( + rd, + Q, + Eigen::Map(&coil->rmag(p, 0)), + Eigen::Map(&coil->cosmag(p, 0)), + ee); } /* * Volume current contribution */ - for (k = 0; k < coils->ncoil; k++) - grad[k] = grad[k] + mne_dot_vectors_40(sol->solution[k],v0,m->nsol); + for (k = 0; k < coils.ncoil(); k++) + grad[k] = grad[k] + sol->solution.row(k).dot(v0); /* * Scale correctly */ - for (k = 0; k < coils->ncoil; k++) + for (k = 0; k < coils.ncoil(); k++) grad[k] = MAG_FACTOR*grad[k]; } return; @@ -2698,120 +2346,111 @@ void FwdBemModel::fwd_bem_field_grad_calc(float *rd, float *Q, FwdCoilSet* coils //============================================================================================================= -float FwdBemModel::fwd_bem_inf_field_der(float *rd, float *Q, float *rp, float *dir, float *comp) /* Which gradient component */ +float FwdBemModel::fwd_bem_inf_field_der(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, const Eigen::Vector3f& rp, const Eigen::Vector3f& dir, const Eigen::Vector3f& comp) /* * Derivative of the infinite-medium magnetic field with respect to * one of the dipole position coordinates (without \mu_0/4\pi) */ { - float diff[3],diff2,diff3,diff5,cross[3],crossn[3],res; - - VEC_DIFF_40(rd,rp,diff); - diff2 = VEC_DOT_40(diff,diff); - diff3 = sqrt(diff2)*diff2; - diff5 = diff3*diff2; - CROSS_PRODUCT_40(Q,diff,cross); - CROSS_PRODUCT_40(dir,Q,crossn); + Eigen::Vector3f diff = rp - rd; + float diff2 = diff.squaredNorm(); + float diff3 = std::sqrt(diff2) * diff2; + float diff5 = diff3 * diff2; + Eigen::Vector3f cr = Q.cross(diff); + Eigen::Vector3f crn = dir.cross(Q); - res = 3*VEC_DOT_40(cross,dir)*VEC_DOT_40(comp,diff)/diff5 - VEC_DOT_40(comp,crossn)/diff3; - return res; + return 3 * cr.dot(dir) * comp.dot(diff) / diff5 - comp.dot(crn) / diff3; } //============================================================================================================= -float FwdBemModel::fwd_bem_inf_pot_der(float *rd, float *Q, float *rp, float *comp) /* Which gradient component */ +float FwdBemModel::fwd_bem_inf_pot_der(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, const Eigen::Vector3f& rp, const Eigen::Vector3f& comp) /* * Derivative of the infinite-medium potential with respect to one of * the dipole position coordinates */ { - float diff[3]; - float diff2,diff5,diff3; - float res; - - VEC_DIFF_40(rd,rp,diff); - diff2 = VEC_DOT_40(diff,diff); - diff3 = sqrt(diff2)*diff2; - diff5 = diff3*diff2; + Eigen::Vector3f diff = rp - rd; + float diff2 = diff.squaredNorm(); + float diff3 = std::sqrt(diff2) * diff2; + float diff5 = diff3 * diff2; - res = 3*VEC_DOT_40(Q,diff)*VEC_DOT_40(comp,diff)/diff5 - VEC_DOT_40(comp,Q)/diff3; - return (res/(4.0*M_PI)); + float res = 3 * Q.dot(diff) * comp.dot(diff) / diff5 - comp.dot(Q) / diff3; + return res / (4.0 * M_PI); } //============================================================================================================= -void FwdBemModel::fwd_bem_lin_field_grad_calc(float *rd, float *Q, FwdCoilSet *coils, FwdBemModel *m, float *xgrad, float *ygrad, float *zgrad) +void FwdBemModel::fwd_bem_lin_field_grad_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet& coils, Eigen::VectorXf& xgrad, Eigen::VectorXf& ygrad, Eigen::VectorXf& zgrad) /* * Calculate the gradient with respect to dipole position coordinates in a set of coils */ { - FwdBemSolution* sol = (FwdBemSolution*)coils->user_data; + FwdBemSolution* sol = coils.user_data.get(); - float *v0; int s,k,p,np,pp; FwdCoil *coil; float mult; - float ee[3],mri_ee[3],mri_rd[3],mri_Q[3]; - float *grads[3]; - float *grad; - - grads[0] = xgrad; - grads[1] = ygrad; - grads[2] = zgrad; + Eigen::Vector3f ee, mri_ee; + Eigen::Vector3f mri_rd = rd; + Eigen::Vector3f mri_Q = Q; + Eigen::VectorXf* grads[] = {&xgrad, &ygrad, &zgrad}; /* * Space for infinite-medium potentials */ - if (!m->v0) - m->v0 = MALLOC_40(m->nsol,float); - v0 = m->v0; + if (v0.size() == 0) + v0.resize(nsol); + float* v0p = v0.data(); /* * The dipole location and orientation must be transformed */ - VEC_COPY_40(mri_rd,rd); - VEC_COPY_40(mri_Q,Q); - if (!m->head_mri_t.isEmpty()) { - FiffCoordTrans::apply_trans(mri_rd,m->head_mri_t,FIFFV_MOVE); - FiffCoordTrans::apply_trans(mri_Q,m->head_mri_t,FIFFV_NO_MOVE); - } - for (pp = 0; pp < 3; pp++) { - grad = grads[pp]; + if (!head_mri_t.isEmpty()) { + FiffCoordTrans::apply_trans(mri_rd.data(),head_mri_t,FIFFV_MOVE); + FiffCoordTrans::apply_trans(mri_Q.data(),head_mri_t,FIFFV_NO_MOVE); + } + for (pp = X; pp <= Z; pp++) { + Eigen::VectorXf& grad = *grads[pp]; /* * Select the correct gradient component */ - for (p = 0; p < 3; p++) - ee[p] = p == pp ? 1.0 : 0.0; - VEC_COPY_40(mri_ee,ee); - if (!m->head_mri_t.isEmpty()) - FiffCoordTrans::apply_trans(mri_ee,m->head_mri_t,FIFFV_NO_MOVE); + ee = Eigen::Vector3f::Unit(pp); + mri_ee = ee; + if (!head_mri_t.isEmpty()) + FiffCoordTrans::apply_trans(mri_ee.data(),head_mri_t,FIFFV_NO_MOVE); /* * Compute the inifinite-medium potentials at the vertices */ - for (s = 0, p = 0; s < m->nsurf; s++) { - np = m->surfs[s]->np; - mult = m->source_mult[s]; + for (s = 0, p = 0; s < nsurf; s++) { + np = surfs[s]->np; + mult = source_mult[s]; for (k = 0; k < np; k++) - v0[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,&m->surfs[s]->rr(k,0),mri_ee); + v0p[p++] = mult*fwd_bem_inf_pot_der(mri_rd,mri_Q,Eigen::Map(&surfs[s]->rr(k,X)),mri_ee); } /* * Primary current contribution * (can be calculated in the coil/dipole coordinates) */ - for (k = 0; k < coils->ncoil; k++) { - coil = coils->coils[k]; + for (k = 0; k < coils.ncoil(); k++) { + coil = coils.coils[k].get(); grad[k] = 0.0; for (p = 0; p < coil->np; p++) - grad[k] = grad[k] + coil->w[p]*fwd_bem_inf_field_der(rd,Q,coil->rmag[p],coil->cosmag[p],ee); + grad[k] = grad[k] + coil->w[p]*fwd_bem_inf_field_der( + rd, + Q, + Eigen::Map(&coil->rmag(p, 0)), + Eigen::Map(&coil->cosmag(p, 0)), + ee); } /* * Volume current contribution */ - for (k = 0; k < coils->ncoil; k++) - grad[k] = grad[k] + mne_dot_vectors_40(sol->solution[k],v0,m->nsol); + for (k = 0; k < coils.ncoil(); k++) + grad[k] = grad[k] + sol->solution.row(k).dot(v0); /* * Scale correctly */ - for (k = 0; k < coils->ncoil; k++) + for (k = 0; k < coils.ncoil(); k++) grad[k] = MAG_FACTOR*grad[k]; } return; @@ -2819,7 +2458,7 @@ void FwdBemModel::fwd_bem_lin_field_grad_calc(float *rd, float *Q, FwdCoilSet *c //============================================================================================================= -int FwdBemModel::fwd_bem_field(float *rd, float *Q, FwdCoilSet *coils, float *B, void *client) /* The model */ +int FwdBemModel::fwd_bem_field(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet *coils, float *B, void *client) /* The model */ /* * This version calculates the magnetic field in a set of coils * Call fwd_bem_specify_coils first to establish the coil-specific @@ -2827,20 +2466,26 @@ int FwdBemModel::fwd_bem_field(float *rd, float *Q, FwdCoilSet *coils, float *B, */ { FwdBemModel* m = (FwdBemModel*)client; - FwdBemSolution* sol = (FwdBemSolution*)coils->user_data; + FwdBemSolution* sol = coils->user_data.get(); if (!m) { printf("No BEM model specified to fwd_bem_field"); return FAIL; } - if (!sol || !sol->solution || sol->ncoil != coils->ncoil) { + if (!sol || sol->solution.size() == 0 || sol->ncoil != coils->ncoil()) { printf("No appropriate coil-specific data available in fwd_bem_field"); return FAIL; } - if (m->bem_method == FWD_BEM_CONSTANT_COLL) - fwd_bem_field_calc(rd,Q,coils,m,B); - else if (m->bem_method == FWD_BEM_LINEAR_COLL) - fwd_bem_lin_field_calc(rd,Q,coils,m,B); + if (m->bem_method == FWD_BEM_CONSTANT_COLL) { + Eigen::VectorXf Bvec(coils->ncoil()); + m->fwd_bem_field_calc(rd,Q,*coils,Bvec); + Eigen::Map(B, coils->ncoil()) = Bvec; + } + else if (m->bem_method == FWD_BEM_LINEAR_COLL) { + Eigen::VectorXf Bvec(coils->ncoil()); + m->fwd_bem_lin_field_calc(rd,Q,*coils,Bvec); + Eigen::Map(B, coils->ncoil()) = Bvec; + } else { printf("Unknown BEM method : %d",m->bem_method); return FAIL; @@ -2850,8 +2495,8 @@ int FwdBemModel::fwd_bem_field(float *rd, float *Q, FwdCoilSet *coils, float *B, //============================================================================================================= -int FwdBemModel::fwd_bem_field_grad(float *rd, - float Q[], +int FwdBemModel::fwd_bem_field_grad(const Eigen::Vector3f& rd, + const Eigen::Vector3f& Q, FwdCoilSet *coils, float Bval[], float xgrad[], @@ -2860,50 +2505,44 @@ int FwdBemModel::fwd_bem_field_grad(float *rd, void *client) /* Client data to be passed to some foward modelling routines */ { FwdBemModel* m = (FwdBemModel*)client; - FwdBemSolution* sol = (FwdBemSolution*)coils->user_data; + FwdBemSolution* sol = coils->user_data.get(); if (!m) { qCritical("No BEM model specified to fwd_bem_field"); return FAIL; } - if (!sol || !sol->solution || sol->ncoil != coils->ncoil) { + if (!sol || sol->solution.size() == 0 || sol->ncoil != coils->ncoil()) { qCritical("No appropriate coil-specific data available in fwd_bem_field"); return FAIL; } if (m->bem_method == FWD_BEM_CONSTANT_COLL) { + int n = coils->ncoil(); if (Bval) { - fwd_bem_field_calc(rd, - Q, - coils, - m, - Bval); + Eigen::VectorXf Bvec(n); + m->fwd_bem_field_calc(rd,Q,*coils,Bvec); + Eigen::Map(Bval, n) = Bvec; } - fwd_bem_field_grad_calc(rd, - Q, - coils, - m, - xgrad, - ygrad, - zgrad); + Eigen::VectorXf xg(n), yg(n), zg(n); + m->fwd_bem_field_grad_calc(rd,Q,*coils,xg,yg,zg); + Eigen::Map(xgrad, n) = xg; + Eigen::Map(ygrad, n) = yg; + Eigen::Map(zgrad, n) = zg; } else if (m->bem_method == FWD_BEM_LINEAR_COLL) { + int n = coils->ncoil(); if (Bval) { - fwd_bem_lin_field_calc(rd, - Q, - coils, - m, - Bval); + Eigen::VectorXf Bvec(n); + m->fwd_bem_lin_field_calc(rd,Q,*coils,Bvec); + Eigen::Map(Bval, n) = Bvec; } - fwd_bem_lin_field_grad_calc(rd, - Q, - coils, - m, - xgrad, - ygrad, - zgrad); + Eigen::VectorXf xg(n), yg(n), zg(n); + m->fwd_bem_lin_field_grad_calc(rd,Q,*coils,xg,yg,zg); + Eigen::Map(xgrad, n) = xg; + Eigen::Map(ygrad, n) = yg; + Eigen::Map(zgrad, n) = zg; } else { qCritical("Unknown BEM method : %d",m->bem_method); return FAIL; @@ -2914,13 +2553,12 @@ int FwdBemModel::fwd_bem_field_grad(float *rd, //============================================================================================================= -void *FwdBemModel::meg_eeg_fwd_one_source_space(void *arg) +void FwdBemModel::meg_eeg_fwd_one_source_space(FwdThreadArg* a) /* * Compute the MEG or EEG forward solution for one source space * and possibly for only one source component */ { - FwdThreadArg* a = (FwdThreadArg*)arg; MNESourceSpace* s = a->s; int j,p,q; float *xyz[3]; @@ -2931,8 +2569,8 @@ void *FwdBemModel::meg_eeg_fwd_one_source_space(void *arg) if (a->field_pot_grad && a->res_grad) { /* Gradient requested? */ for (j = 0; j < s->np; j++) { if (s->inuse[j]) { - if (a->field_pot_grad(&s->rr(j,0), - &s->nn(j,0), + if (a->field_pot_grad(Eigen::Map(&s->rr(j,0)), + Eigen::Map(&s->nn(j,0)), a->coils_els, a->res[p], a->res_grad[q], @@ -2948,8 +2586,8 @@ void *FwdBemModel::meg_eeg_fwd_one_source_space(void *arg) } else { for (j = 0; j < s->np; j++) if (s->inuse[j]) - if (a->field_pot(&s->rr(j,0), - &s->nn(j,0), + if (a->field_pot(Eigen::Map(&s->rr(j,0)), + Eigen::Map(&s->nn(j,0)), a->coils_els, a->res[p++], a->client) != OK) @@ -2961,7 +2599,7 @@ void *FwdBemModel::meg_eeg_fwd_one_source_space(void *arg) for (j = 0; j < s->np; j++) { if (s->inuse[j]) { if (a->comp < 0) { /* Compute all components */ - if (a->field_pot_grad(&s->rr(j,0), + if (a->field_pot_grad(Eigen::Map(&s->rr(j,0)), Qx, a->coils_els, a->res[p], @@ -2971,7 +2609,7 @@ void *FwdBemModel::meg_eeg_fwd_one_source_space(void *arg) a->client) != OK) goto bad; q = q + 3; p++; - if (a->field_pot_grad(&s->rr(j,0), + if (a->field_pot_grad(Eigen::Map(&s->rr(j,0)), Qy, a->coils_els, a->res[p], @@ -2981,7 +2619,7 @@ void *FwdBemModel::meg_eeg_fwd_one_source_space(void *arg) a->client) != OK) goto bad; q = q + 3; p++; - if (a->field_pot_grad(&s->rr(j,0), + if (a->field_pot_grad(Eigen::Map(&s->rr(j,0)), Qz, a->coils_els, a->res[p], @@ -2993,7 +2631,7 @@ void *FwdBemModel::meg_eeg_fwd_one_source_space(void *arg) q = q + 3; p++; } else if (a->comp == 0) { /* Compute x component */ - if (a->field_pot_grad(&s->rr(j,0), + if (a->field_pot_grad(Eigen::Map(&s->rr(j,0)), Qx, a->coils_els, a->res[p], @@ -3008,7 +2646,7 @@ void *FwdBemModel::meg_eeg_fwd_one_source_space(void *arg) } else if (a->comp == 1) { /* Compute y component */ q = q + 3; p++; - if (a->field_pot_grad(&s->rr(j,0), + if (a->field_pot_grad(Eigen::Map(&s->rr(j,0)), Qy, a->coils_els, a->res[p], @@ -3023,7 +2661,7 @@ void *FwdBemModel::meg_eeg_fwd_one_source_space(void *arg) else if (a->comp == 2) { /* Compute z component */ q = q + 3; p++; q = q + 3; p++; - if (a->field_pot_grad(&s->rr(j,0), + if (a->field_pot_grad(Eigen::Map(&s->rr(j,0)), Qz, a->coils_els, a->res[p], @@ -3044,32 +2682,32 @@ void *FwdBemModel::meg_eeg_fwd_one_source_space(void *arg) xyz[0] = a->res[p++]; xyz[1] = a->res[p++]; xyz[2] = a->res[p++]; - if (a->vec_field_pot(&s->rr(j,0),a->coils_els,xyz,a->client) != OK) + if (a->vec_field_pot(Eigen::Map(&s->rr(j,0)),a->coils_els,xyz,a->client) != OK) goto bad; } else { if (a->comp < 0) { /* Compute all components here */ - if (a->field_pot(&s->rr(j,0),Qx,a->coils_els,a->res[p++],a->client) != OK) + if (a->field_pot(Eigen::Map(&s->rr(j,0)),Qx,a->coils_els,a->res[p++],a->client) != OK) goto bad; - if (a->field_pot(&s->rr(j,0),Qy,a->coils_els,a->res[p++],a->client) != OK) + if (a->field_pot(Eigen::Map(&s->rr(j,0)),Qy,a->coils_els,a->res[p++],a->client) != OK) goto bad; - if (a->field_pot(&s->rr(j,0),Qz,a->coils_els,a->res[p++],a->client) != OK) + if (a->field_pot(Eigen::Map(&s->rr(j,0)),Qz,a->coils_els,a->res[p++],a->client) != OK) goto bad; } else if (a->comp == 0) { /* Compute x component */ - if (a->field_pot(&s->rr(j,0),Qx,a->coils_els,a->res[p++],a->client) != OK) + if (a->field_pot(Eigen::Map(&s->rr(j,0)),Qx,a->coils_els,a->res[p++],a->client) != OK) goto bad; p++; p++; } else if (a->comp == 1) { /* Compute y component */ p++; - if (a->field_pot(&s->rr(j,0),Qy,a->coils_els,a->res[p++],a->client) != OK) + if (a->field_pot(Eigen::Map(&s->rr(j,0)),Qy,a->coils_els,a->res[p++],a->client) != OK) goto bad; p++; } else if (a->comp == 2) { /* Compute z component */ p++; p++; - if (a->field_pot(&s->rr(j,0),Qz,a->coils_els,a->res[p++],a->client) != OK) + if (a->field_pot(Eigen::Map(&s->rr(j,0)),Qz,a->coils_els,a->res[p++],a->client) != OK) goto bad; } } @@ -3078,11 +2716,11 @@ void *FwdBemModel::meg_eeg_fwd_one_source_space(void *arg) } } a->stat = OK; - return NULL; + return; bad : { a->stat = FAIL; - return NULL; + return; } } @@ -3093,8 +2731,7 @@ int FwdBemModel::compute_forward_meg(std::vector FwdCoilSet *comp_coils, MNECTFCompDataSet *comp_data, bool fixed_ori, - FwdBemModel *bem_model, - Vector3f *r0, + const Vector3f &r0, bool use_threads, FiffNamedMatrix& resp, FiffNamedMatrix& resp_grad, @@ -3106,6 +2743,10 @@ int FwdBemModel::compute_forward_meg(std::vector { float **res = NULL; /* The forward solution matrix */ float **res_grad = NULL; /* The gradient with respect to the dipole position */ + std::vector res_data; /* Contiguous storage for res */ + std::vector res_ptrs; /* Row pointers for res */ + std::vector res_grad_data; /* Contiguous storage for res_grad */ + std::vector res_grad_ptrs; /* Row pointers for res_grad */ MatrixXd matRes; MatrixXd matResGrad; int nrow = 0; @@ -3114,7 +2755,7 @@ int FwdBemModel::compute_forward_meg(std::vector fwdVecFieldFunc vec_field; /* Computes the field for all dipole orientations */ fwdFieldGradFunc field_grad; /* Computes the field and gradient with respect to dipole position * for one dipole orientation */ - int nmeg = coils->ncoil;/* Number of channels */ + int nmeg = coils->ncoil();/* Number of channels */ int nsource; /* Total number of sources */ int nspace = static_cast(spaces.size()); int k,p,q,off; @@ -3124,7 +2765,7 @@ int FwdBemModel::compute_forward_meg(std::vector int nproc = QThread::idealThreadCount(); QStringList emptyList; - if (bem_model) { + if (true) { /* * Use the new compensated field computation * It works the same way independent of whether or not the compensation is in effect @@ -3134,19 +2775,17 @@ int FwdBemModel::compute_forward_meg(std::vector comp = FwdCompData::fwd_make_comp_data(comp_data, coils,comp_coils, FwdBemModel::fwd_bem_field, - NULL, + nullptr, my_bem_field_grad, - bem_model, - NULL); + this); #else comp = FwdCompData::fwd_make_comp_data(comp_data, coils, comp_coils, FwdBemModel::fwd_bem_field, - NULL, + nullptr, FwdBemModel::fwd_bem_field_grad, - bem_model, - NULL); + this); #endif if (!comp) goto bad; @@ -3155,18 +2794,18 @@ int FwdBemModel::compute_forward_meg(std::vector */ qDebug() << "!!!TODO Speed the following with Eigen up!"; printf("Composing the field computation matrix..."); - if (fwd_bem_specify_coils(bem_model,coils) == FAIL) + if (fwd_bem_specify_coils(coils) == FAIL) goto bad; printf("[done]\n"); if (comp->set && comp->set->current) { /* Test just to specify confusing output */ printf("Composing the field computation matrix (compensation coils)..."); - if (fwd_bem_specify_coils(bem_model,comp->comp_coils) == FAIL) + if (fwd_bem_specify_coils(comp->comp_coils) == FAIL) goto bad; printf("[done]\n"); } field = FwdCompData::fwd_comp_field; - vec_field = NULL; + vec_field = nullptr; field_grad = FwdCompData::fwd_comp_field_grad; client = comp; } @@ -3181,13 +2820,13 @@ int FwdBemModel::compute_forward_meg(std::vector fwd_sphere_field, fwd_sphere_field_vec, my_sphere_field_grad, - r0,NULL); + const_cast(&r0)); #else comp = FwdCompData::fwd_make_comp_data(comp_data,coils,comp_coils, fwd_sphere_field, fwd_sphere_field_vec, fwd_sphere_field_grad, - r0,NULL); + const_cast(&r0)); #endif if (!comp) goto bad; @@ -3204,15 +2843,21 @@ int FwdBemModel::compute_forward_meg(std::vector /* * Allocate space for the solution */ - if (fixed_ori) - res = ALLOC_CMATRIX_40(nsource,nmeg); - else - res = ALLOC_CMATRIX_40(3*nsource,nmeg); + { + int res_rows = fixed_ori ? nsource : 3*nsource; + res_data.assign(res_rows * nmeg, 0.0f); + res_ptrs.resize(res_rows); + for (int i = 0; i < res_rows; i++) + res_ptrs[i] = res_data.data() + i * nmeg; + res = res_ptrs.data(); + } if (bDoGrad) { - if (fixed_ori) - res_grad = ALLOC_CMATRIX_40(3*nsource,nmeg); - else - res_grad = ALLOC_CMATRIX_40(3*3*nsource,nmeg); + int grad_rows = fixed_ori ? 3*nsource : 3*3*nsource; + res_grad_data.assign(grad_rows * nmeg, 0.0f); + res_grad_ptrs.resize(grad_rows); + for (int i = 0; i < grad_rows; i++) + res_grad_ptrs[i] = res_grad_data.data() + i * nmeg; + res_grad = res_grad_ptrs.data(); } /* * Set up the argument for the field computation @@ -3234,14 +2879,14 @@ int FwdBemModel::compute_forward_meg(std::vector if (use_threads) { int nthread = (fixed_ori || vec_field || nproc < 6) ? nspace : 3*nspace; - QList args; //fwdThreadArg *args = MALLOC_40(nthread,fwdThreadArg); + QList args; int stat; /* * We need copies to allocate separate workspace for each thread */ if (fixed_ori || vec_field || nproc < 6) { for (k = 0, off = 0; k < nthread; k++) { - FwdThreadArg* t_arg = FwdThreadArg::create_meg_multi_thread_duplicate(one_arg,bem_model != NULL); + FwdThreadArg* t_arg = FwdThreadArg::create_meg_multi_thread_duplicate(one_arg,true); t_arg->s = spaces[k].get(); t_arg->off = off; off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse; @@ -3253,7 +2898,7 @@ int FwdBemModel::compute_forward_meg(std::vector else { for (k = 0, off = 0, q = 0; k < nspace; k++) { for (p = 0; p < 3; p++,q++) { - FwdThreadArg* t_arg = FwdThreadArg::create_meg_multi_thread_duplicate(one_arg,bem_model != NULL); + FwdThreadArg* t_arg = FwdThreadArg::create_meg_multi_thread_duplicate(one_arg,true); t_arg->s = spaces[k].get(); t_arg->off = off; t_arg->comp = p; @@ -3279,7 +2924,7 @@ int FwdBemModel::compute_forward_meg(std::vector break; } for (k = 0; k < args.size(); k++)//nthread == args.size() - FwdThreadArg::free_meg_multi_thread_duplicate(args[k],bem_model != NULL); + FwdThreadArg::free_meg_multi_thread_duplicate(args[k],true); if (stat != OK) goto bad; } @@ -3344,8 +2989,6 @@ bad : { delete one_arg; if(comp) delete comp; - FREE_CMATRIX_40(res); - FREE_CMATRIX_40(res_grad); return FAIL; } } @@ -3355,8 +2998,7 @@ bad : { int FwdBemModel::compute_forward_eeg(std::vector>& spaces, FwdCoilSet *els, bool fixed_ori, - FwdBemModel *bem_model, - FwdEegSphereModel *m, + FwdEegSphereModel *eeg_model, bool use_threads, FiffNamedMatrix& resp, FiffNamedMatrix& resp_grad, @@ -3368,6 +3010,10 @@ int FwdBemModel::compute_forward_eeg(std::vector { float **res = NULL; /* The forward solution matrix */ float **res_grad = NULL; /* The gradient with respect to the dipole position */ + std::vector res_data; /* Contiguous storage for res */ + std::vector res_ptrs; /* Row pointers for res */ + std::vector res_grad_data; /* Contiguous storage for res_grad */ + std::vector res_grad_ptrs; /* Row pointers for res_grad */ MatrixXd matRes; MatrixXd matResGrad; int nrow = 0; @@ -3377,7 +3023,7 @@ int FwdBemModel::compute_forward_eeg(std::vector * for one dipole orientation */ int nsource; /* Total number of sources */ int nspace = static_cast(spaces.size()); - int neeg = els->ncoil; /* Number of channels */ + int neeg = els->ncoil(); /* Number of channels */ int k,p,q,off; QStringList names; /* Channel names */ void *client; @@ -3390,12 +3036,12 @@ int FwdBemModel::compute_forward_eeg(std::vector for (k = 0, nsource = 0; k < nspace; k++) nsource += spaces[k]->nuse; - if (bem_model) { - if (fwd_bem_specify_els(bem_model,els) == FAIL) + if (true) { + if (fwd_bem_specify_els(els) == FAIL) goto bad; - client = bem_model; + client = this; pot = fwd_bem_pot_els; - vec_pot = NULL; + vec_pot = nullptr; #ifdef TEST printf("Using differences.\n"); pot_grad = my_bem_pot_grad; @@ -3404,11 +3050,11 @@ int FwdBemModel::compute_forward_eeg(std::vector #endif } else { - if (m->nfit == 0) { + if (eeg_model->nfit == 0) { printf("Using the standard series expansion for a multilayer sphere model for EEG\n"); pot = FwdEegSphereModel::fwd_eeg_multi_spherepot_coil1; - vec_pot = NULL; - pot_grad = NULL; + vec_pot = nullptr; + pot_grad = nullptr; } else { printf("Using the equivalent source approach in the homogeneous sphere for EEG\n"); @@ -3416,24 +3062,30 @@ int FwdBemModel::compute_forward_eeg(std::vector vec_pot = FwdEegSphereModel::fwd_eeg_spherepot_coil_vec; pot_grad = FwdEegSphereModel::fwd_eeg_spherepot_grad_coil; } - client = m; + client = eeg_model; } /* * Allocate space for the solution */ - if (fixed_ori) - res = ALLOC_CMATRIX_40(nsource,neeg); - else - res = ALLOC_CMATRIX_40(3*nsource,neeg); + { + int res_rows = fixed_ori ? nsource : 3*nsource; + res_data.assign(res_rows * neeg, 0.0f); + res_ptrs.resize(res_rows); + for (int i = 0; i < res_rows; i++) + res_ptrs[i] = res_data.data() + i * neeg; + res = res_ptrs.data(); + } if (bDoGrad) { if (!pot_grad) { qCritical("EEG gradient calculation function not available"); goto bad; } - if (fixed_ori) - res_grad = ALLOC_CMATRIX_40(3*nsource,neeg); - else - res_grad = ALLOC_CMATRIX_40(3*3*nsource,neeg); + int grad_rows = fixed_ori ? 3*nsource : 3*3*nsource; + res_grad_data.assign(grad_rows * neeg, 0.0f); + res_grad_ptrs.resize(grad_rows); + for (int i = 0; i < grad_rows; i++) + res_grad_ptrs[i] = res_grad_data.data() + i * neeg; + res_grad = res_grad_ptrs.data(); } /* * Set up the argument for the field computation @@ -3455,14 +3107,14 @@ int FwdBemModel::compute_forward_eeg(std::vector if (use_threads) { int nthread = (fixed_ori || vec_pot || nproc < 6) ? nspace : 3*nspace; - QList args; //FwdThreadArg* *args = MALLOC_40(nthread,FwdThreadArg*); + QList args; int stat; /* * We need copies to allocate separate workspace for each thread */ if (fixed_ori || vec_pot || nproc < 6) { for (k = 0, off = 0; k < nthread; k++) { - FwdThreadArg* t_arg = FwdThreadArg::create_eeg_multi_thread_duplicate(one_arg,bem_model != NULL); + FwdThreadArg* t_arg = FwdThreadArg::create_eeg_multi_thread_duplicate(one_arg,true); t_arg->s = spaces[k].get(); t_arg->off = off; off = fixed_ori ? off + spaces[k]->nuse : off + 3*spaces[k]->nuse; @@ -3473,7 +3125,7 @@ int FwdBemModel::compute_forward_eeg(std::vector else { for (k = 0, off = 0, q = 0; k < nspace; k++) { for (p = 0; p < 3; p++,q++) { - FwdThreadArg* t_arg = FwdThreadArg::create_eeg_multi_thread_duplicate(one_arg,bem_model != NULL); + FwdThreadArg* t_arg = FwdThreadArg::create_eeg_multi_thread_duplicate(one_arg,true); t_arg->s = spaces[k].get(); t_arg->off = off; t_arg->comp = p; @@ -3498,7 +3150,7 @@ int FwdBemModel::compute_forward_eeg(std::vector break; } for (k = 0; k < args.size(); k++)//nthread == args.size() - FwdThreadArg::free_eeg_multi_thread_duplicate(args[k],bem_model != NULL); + FwdThreadArg::free_eeg_multi_thread_duplicate(args[k],true); if (stat != OK) goto bad; } @@ -3558,8 +3210,6 @@ int FwdBemModel::compute_forward_eeg(std::vector bad : { if(one_arg) delete one_arg; - FREE_CMATRIX_40(res); - FREE_CMATRIX_40(res_grad); return FAIL; } } @@ -3571,7 +3221,7 @@ bad : { origin */ #define CEPS 1e-5 -int FwdBemModel::fwd_sphere_field(float *rd, float Q[], FwdCoilSet *coils, float Bval[], void *client) /* Client data will be the sphere model origin */ +int FwdBemModel::fwd_sphere_field(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet *coils, float Bval[], void *client) /* Client data will be the sphere model origin */ { /* This version uses Jukka Sarvas' field computation for details, see @@ -3604,28 +3254,27 @@ int FwdBemModel::fwd_sphere_field(float *rd, float Q[], FwdCoilSet *coils, float */ for (p = 0; p < 3; p++) myrd[p] = rd[p] - r0[p]; - rd = myrd; /* * Check for a dipole at the origin */ - for (k = 0 ; k < coils->ncoil ; k++) + for (k = 0 ; k < coils->ncoil() ; k++) if (FWD_IS_MEG_COIL(coils->coils[k]->coil_class)) Bval[k] = 0.0; - r = VEC_LEN_40(rd); + r = vec_len(myrd); if (r > EPS) { /* The hard job */ - CROSS_PRODUCT_40(Q,rd,v); + cross_product(Q,myrd,v); - for (k = 0; k < coils->ncoil; k++) { - this_coil = coils->coils[k]; + for (k = 0; k < coils->ncoil(); k++) { + this_coil = coils->coils[k].get(); if (FWD_IS_MEG_COIL(this_coil->type)) { np = this_coil->np; for (j = 0, sum = 0.0; j < np; j++) { - this_pos = this_coil->rmag[j]; - this_dir = this_coil->cosmag[j]; + this_pos = &this_coil->rmag(j, 0); + this_dir = &this_coil->cosmag(j, 0); for (p = 0; p < 3; p++) pos[p] = this_pos[p] - r0[p]; @@ -3634,23 +3283,23 @@ int FwdBemModel::fwd_sphere_field(float *rd, float Q[], FwdCoilSet *coils, float /* Vector from dipole to the field point */ - VEC_DIFF_40(rd,this_pos,a_vec); + vec_diff(myrd,this_pos,a_vec); /* Compute the dot products needed */ - a2 = VEC_DOT_40(a_vec,a_vec); a = sqrt(a2); + a2 = vec_dot(a_vec,a_vec); a = sqrt(a2); if (a > 0.0) { - r2 = VEC_DOT_40(this_pos,this_pos); r = sqrt(r2); + r2 = vec_dot(this_pos,this_pos); r = sqrt(r2); if (r > 0.0) { - rr0 = VEC_DOT_40(this_pos,rd); + rr0 = vec_dot(this_pos,myrd); ar = (r2-rr0); if (std::fabs(ar/(a*r)+1.0) > CEPS) { /* There is a problem on the negative 'z' axis if the dipole location * and the field point are on the same line */ ar0 = ar/a; - ve = VEC_DOT_40(v,this_dir); vr = VEC_DOT_40(v,this_pos); - re = VEC_DOT_40(this_pos,this_dir); r0e = VEC_DOT_40(rd,this_dir); + ve = vec_dot(v,this_dir); vr = vec_dot(v,this_pos); + re = vec_dot(this_pos,this_dir); r0e = vec_dot(rd,this_dir); /* The main ingredients */ @@ -3674,7 +3323,7 @@ int FwdBemModel::fwd_sphere_field(float *rd, float Q[], FwdCoilSet *coils, float //============================================================================================================= -int FwdBemModel::fwd_sphere_field_vec(float *rd, FwdCoilSet *coils, float **Bval, void *client) /* Client data will be the sphere model origin */ +int FwdBemModel::fwd_sphere_field_vec(const Eigen::Vector3f& rd, FwdCoilSet *coils, float **Bval, void *client) /* Client data will be the sphere model origin */ { /* This version uses Jukka Sarvas' field computation for details, see @@ -3713,13 +3362,12 @@ int FwdBemModel::fwd_sphere_field_vec(float *rd, FwdCoilSet *coils, float **Bval */ for (p = 0; p < 3; p++) myrd[p] = rd[p] - r0[p]; - rd = myrd; /* * Check for a dipole at the origin */ - r = VEC_LEN_40(rd); - for (k = 0; k < coils->ncoil; k++) { - this_coil = coils->coils[k]; + r = vec_len(myrd); + for (k = 0; k < coils->ncoil(); k++) { + this_coil = coils->coils[k].get(); if (FWD_IS_MEG_COIL(this_coil->coil_class)) { if (r < EPS) { Bval[0][k] = Bval[1][k] = Bval[2][k] = 0.0; @@ -3731,8 +3379,8 @@ int FwdBemModel::fwd_sphere_field_vec(float *rd, FwdCoilSet *coils, float **Bval for (j = 0; j < np; j++) { - this_pos = this_coil->rmag[j]; - this_dir = this_coil->cosmag[j]; + this_pos = &this_coil->rmag(j, 0); + this_dir = &this_coil->cosmag(j, 0); for (p = 0; p < 3; p++) pos[p] = this_pos[p] - r0[p]; @@ -3740,16 +3388,16 @@ int FwdBemModel::fwd_sphere_field_vec(float *rd, FwdCoilSet *coils, float **Bval /* Vector from dipole to the field point */ - VEC_DIFF_40(rd,this_pos,a_vec); + vec_diff(myrd,this_pos,a_vec); /* Compute the dot products needed */ - a2 = VEC_DOT_40(a_vec,a_vec); a = sqrt(a2); + a2 = vec_dot(a_vec,a_vec); a = sqrt(a2); if (a > 0.0) { - r2 = VEC_DOT_40(this_pos,this_pos); r = sqrt(r2); + r2 = vec_dot(this_pos,this_pos); r = sqrt(r2); if (r > 0.0) { - rr0 = VEC_DOT_40(this_pos,rd); + rr0 = vec_dot(this_pos,myrd); ar = (r2-rr0); if (std::fabs(ar/(a*r)+1.0) > CEPS) { /* There is a problem on the negative 'z' axis if the dipole location * and the field point are on the same line */ @@ -3761,9 +3409,9 @@ int FwdBemModel::fwd_sphere_field_vec(float *rd, FwdCoilSet *coils, float **Bval gr = a2/r + ar0 + 2.0*(a+r); g0 = a + 2*r + ar0; - re = VEC_DOT_40(this_pos,this_dir); r0e = VEC_DOT_40(rd,this_dir); - CROSS_PRODUCT_40(rd,this_dir,v1); - CROSS_PRODUCT_40(rd,this_pos,v2); + re = vec_dot(this_pos,this_dir); r0e = vec_dot(myrd,this_dir); + cross_product(myrd,this_dir,v1); + cross_product(myrd,this_pos,v2); g = (g0*r0e - gr*re)/(F*F); /* @@ -3785,7 +3433,7 @@ int FwdBemModel::fwd_sphere_field_vec(float *rd, FwdCoilSet *coils, float **Bval //============================================================================================================= -int FwdBemModel::fwd_sphere_field_grad(float *rd, float Q[], FwdCoilSet *coils, float Bval[], float xgrad[], float ygrad[], float zgrad[], void *client) /* Client data to be passed to some foward modelling routines */ +int FwdBemModel::fwd_sphere_field_grad(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet *coils, float Bval[], float xgrad[], float ygrad[], float zgrad[], void *client) /* Client data to be passed to some foward modelling routines */ /* * Compute the derivatives of the sphere model field with respect to * dipole coordinates @@ -3832,15 +3480,14 @@ int FwdBemModel::fwd_sphere_field_grad(float *rd, float Q[], FwdCoilSet *coils, */ for (p = 0; p < 3; p++) myrd[p] = rd[p] - r0[p]; - rd = myrd; if (Bval) do_field = 1; /* Check for a dipole at the origin */ - r = VEC_LEN_40(rd); - for (k = 0; k < coils->ncoil ; k++) { + r = vec_len(myrd); + for (k = 0; k < coils->ncoil() ; k++) { if (FWD_IS_MEG_COIL(coils->coils[k]->coil_class)) { if (do_field) Bval[k] = 0.0; @@ -3851,13 +3498,13 @@ int FwdBemModel::fwd_sphere_field_grad(float *rd, float Q[], FwdCoilSet *coils, } if (r > EPS) { /* The hard job */ - v[X_40] = Q[Y_40]*rd[Z_40] - Q[Z_40]*rd[Y_40]; - v[Y_40] = -Q[X_40]*rd[Z_40] + Q[Z_40]*rd[X_40]; - v[Z_40] = Q[X_40]*rd[Y_40] - Q[Y_40]*rd[X_40]; + v[0] = Q[1]*myrd[2] - Q[2]*myrd[1]; + v[1] = -Q[0]*myrd[2] + Q[2]*myrd[0]; + v[2] = Q[0]*myrd[1] - Q[1]*myrd[0]; - for (k = 0 ; k < coils->ncoil ; k++) { + for (k = 0 ; k < coils->ncoil() ; k++) { - this_coil = coils->coils[k]; + this_coil = coils->coils[k].get(); if (FWD_IS_MEG_COIL(this_coil->type)) { @@ -3865,7 +3512,7 @@ int FwdBemModel::fwd_sphere_field_grad(float *rd, float Q[], FwdCoilSet *coils, for (j = 0; j < np; j++) { - this_pos = this_coil->rmag[j]; + this_pos = &this_coil->rmag(j, 0); /* * Shift to the sphere model coordinates */ @@ -3873,35 +3520,35 @@ int FwdBemModel::fwd_sphere_field_grad(float *rd, float Q[], FwdCoilSet *coils, pos[p] = this_pos[p] - r0[p]; this_pos = pos; - this_dir = this_coil->cosmag[j]; + this_dir = &this_coil->cosmag(j, 0); /* Vector from dipole to the field point */ - a_vec[X_40] = this_pos[X_40] - rd[X_40]; - a_vec[Y_40] = this_pos[Y_40] - rd[Y_40]; - a_vec[Z_40] = this_pos[Z_40] - rd[Z_40]; + a_vec[0] = this_pos[0] - myrd[0]; + a_vec[1] = this_pos[1] - myrd[1]; + a_vec[2] = this_pos[2] - myrd[2]; /* Compute the dot and cross products needed */ - a2 = VEC_DOT_40(a_vec,a_vec); a = sqrt(a2); - r2 = VEC_DOT_40(this_pos,this_pos); r = sqrt(r2); - rr0 = VEC_DOT_40(this_pos,rd); + a2 = vec_dot(a_vec,a_vec); a = sqrt(a2); + r2 = vec_dot(this_pos,this_pos); r = sqrt(r2); + rr0 = vec_dot(this_pos,myrd); ar = (r2 - rr0)/a; - ve = VEC_DOT_40(v,this_dir); vr = VEC_DOT_40(v,this_pos); - re = VEC_DOT_40(this_pos,this_dir); r0e = VEC_DOT_40(rd,this_dir); + ve = vec_dot(v,this_dir); vr = vec_dot(v,this_pos); + re = vec_dot(this_pos,this_dir); r0e = vec_dot(myrd,this_dir); /* eQ = this_dir x Q */ - eQ[X_40] = this_dir[Y_40]*Q[Z_40] - this_dir[Z_40]*Q[Y_40]; - eQ[Y_40] = -this_dir[X_40]*Q[Z_40] + this_dir[Z_40]*Q[X_40]; - eQ[Z_40] = this_dir[X_40]*Q[Y_40] - this_dir[Y_40]*Q[X_40]; + eQ[0] = this_dir[1]*Q[2] - this_dir[2]*Q[1]; + eQ[1] = -this_dir[0]*Q[2] + this_dir[2]*Q[0]; + eQ[2] = this_dir[0]*Q[1] - this_dir[1]*Q[0]; /* rQ = this_pos x Q */ - rQ[X_40] = this_pos[Y_40]*Q[Z_40] - this_pos[Z_40]*Q[Y_40]; - rQ[Y_40] = -this_pos[X_40]*Q[Z_40] + this_pos[Z_40]*Q[X_40]; - rQ[Z_40] = this_pos[X_40]*Q[Y_40] - this_pos[Y_40]*Q[X_40]; + rQ[0] = this_pos[1]*Q[2] - this_pos[2]*Q[1]; + rQ[1] = -this_pos[0]*Q[2] + this_pos[2]*Q[0]; + rQ[2] = this_pos[0]*Q[1] - this_pos[1]*Q[0]; /* The main ingredients */ @@ -3918,7 +3565,7 @@ int FwdBemModel::fwd_sphere_field_grad(float *rd, float Q[], FwdCoilSet *coils, /* The computation of the gradient... */ huu = 2.0 + 2.0*a/r; - for (p = X_40; p <= Z_40; p++) { + for (p = 0; p <= 2; p++) { ga[p] = -a_vec[p]/a; gar[p] = -(ga[p]*ar + this_pos[p])/a; gg0[p] = ga[p] + gar[p]; @@ -3930,9 +3577,9 @@ int FwdBemModel::fwd_sphere_field_grad(float *rd, float Q[], FwdCoilSet *coils, if (do_field) Bval[k] = Bval[k] + this_coil->w[j]*result; - xgrad[k] = xgrad[k] + this_coil->w[j]*gresult[X_40]; - ygrad[k] = ygrad[k] + this_coil->w[j]*gresult[Y_40]; - zgrad[k] = zgrad[k] + this_coil->w[j]*gresult[Z_40]; + xgrad[k] = xgrad[k] + this_coil->w[j]*gresult[0]; + ygrad[k] = ygrad[k] + this_coil->w[j]*gresult[1]; + zgrad[k] = zgrad[k] + this_coil->w[j]*gresult[2]; } if (do_field) Bval[k] = MAG_FACTOR*Bval[k]; @@ -3947,7 +3594,7 @@ int FwdBemModel::fwd_sphere_field_grad(float *rd, float Q[], FwdCoilSet *coils, //============================================================================================================= -int FwdBemModel::fwd_mag_dipole_field(float *rm, float M[], FwdCoilSet *coils, float Bval[], void *client) /* Client data will be the sphere model origin */ +int FwdBemModel::fwd_mag_dipole_field(const Eigen::Vector3f& rm, const Eigen::Vector3f& M, FwdCoilSet *coils, float Bval[], void *client) /* Client data will be the sphere model origin */ /* * This is for a specific dipole component */ @@ -3956,21 +3603,21 @@ int FwdBemModel::fwd_mag_dipole_field(float *rm, float M[], FwdCoilSet *coils, f FwdCoil* this_coil; float sum,diff[3],dist,dist2,dist5,*dir; - for (k = 0; k < coils->ncoil; k++) { - this_coil = coils->coils[k]; + for (k = 0; k < coils->ncoil(); k++) { + this_coil = coils->coils[k].get(); if (FWD_IS_MEG_COIL(this_coil->type)) { np = this_coil->np; /* * Go through all points */ for (j = 0, sum = 0.0; j < np; j++) { - dir = this_coil->cosmag[j]; - VEC_DIFF_40(rm,this_coil->rmag[j],diff); - dist = VEC_LEN_40(diff); + dir = &this_coil->cosmag(j, 0); + vec_diff(rm,&this_coil->rmag(j, 0),diff); + dist = vec_len(diff); if (dist > EPS) { dist2 = dist*dist; dist5 = dist2*dist2*dist; - sum = sum + this_coil->w[j]*(3*VEC_DOT_40(M,diff)*VEC_DOT_40(diff,dir) - dist2*VEC_DOT_40(M,dir))/dist5; + sum = sum + this_coil->w[j]*(3*vec_dot(M,diff)*vec_dot(diff,dir) - dist2*vec_dot(M,dir))/dist5; } } /* All points done */ Bval[k] = MAG_FACTOR*sum; @@ -3983,7 +3630,7 @@ int FwdBemModel::fwd_mag_dipole_field(float *rm, float M[], FwdCoilSet *coils, f //============================================================================================================= -int FwdBemModel::fwd_mag_dipole_field_vec(float *rm, FwdCoilSet *coils, float **Bval, void *client) /* Client data will be the sphere model origin */ +int FwdBemModel::fwd_mag_dipole_field_vec(const Eigen::Vector3f& rm, FwdCoilSet *coils, float **Bval, void *client) /* Client data will be the sphere model origin */ /* * This is for all dipole components * For EEG this produces a zero result @@ -3993,8 +3640,8 @@ int FwdBemModel::fwd_mag_dipole_field_vec(float *rm, FwdCoilSet *coils, float ** FwdCoil* this_coil; float sum[3],diff[3],dist,dist2,dist5,*dir; - for (k = 0; k < coils->ncoil; k++) { - this_coil = coils->coils[k]; + for (k = 0; k < coils->ncoil(); k++) { + this_coil = coils->coils[k].get(); if (FWD_IS_MEG_COIL(this_coil->type)) { np = this_coil->np; sum[0] = sum[1] = sum[2] = 0.0; @@ -4002,14 +3649,14 @@ int FwdBemModel::fwd_mag_dipole_field_vec(float *rm, FwdCoilSet *coils, float ** * Go through all points */ for (j = 0; j < np; j++) { - dir = this_coil->cosmag[j]; - VEC_DIFF_40(rm,this_coil->rmag[j],diff); - dist = VEC_LEN_40(diff); + dir = &this_coil->cosmag(j, 0); + vec_diff(rm,&this_coil->rmag(j, 0),diff); + dist = vec_len(diff); if (dist > EPS) { dist2 = dist*dist; dist5 = dist2*dist2*dist; for (p = 0; p < 3; p++) - sum[p] = sum[p] + this_coil->w[j]*(3*diff[p]*VEC_DOT_40(diff,dir) - dist2*dir[p])/dist5; + sum[p] = sum[p] + this_coil->w[j]*(3*diff[p]*vec_dot(diff,dir) - dist2*dir[p])/dist5; } } /* All points done */ for (p = 0; p < 3; p++) diff --git a/src/libraries/fwd/fwd_bem_model.h b/src/libraries/fwd/fwd_bem_model.h index 8910f7d889d..c67478b6113 100644 --- a/src/libraries/fwd/fwd_bem_model.h +++ b/src/libraries/fwd/fwd_bem_model.h @@ -34,8 +34,8 @@ * */ -#ifndef FWDBEMMODEL_H -#define FWDBEMMODEL_H +#ifndef FWD_BEM_MODEL_H +#define FWD_BEM_MODEL_H //============================================================================================================= // INCLUDES @@ -103,12 +103,19 @@ namespace FWDLIB //============================================================================================================= class FwdEegSphereModel; +class FwdThreadArg; //============================================================================================================= /** - * Implements FwdBemModel (Replaces *FwdBemModel*,FwdBemModel*Rec struct of MNE-C fwd_types.h). + * @brief BEM (Boundary Element Method) model definition. * - * @brief Holds the BEM model definition + * Holds the BEM model surfaces, conductivity parameters, and potential + * solution matrix used in MEG/EEG forward computations. Surfaces are + * stored from outermost to innermost and owned by this object. + * + * Refactored from the MNE-C fwdBemModel / fwdBemModelRec struct + * (fwd_types.h). Raw C-style arrays replaced with Eigen types; + * surface ownership managed through std::unique_ptr. */ class FWDSHARED_EXPORT FwdBemModel { @@ -118,431 +125,919 @@ class FWDSHARED_EXPORT FwdBemModel //========================================================================================================= /** - * Constructs the BEM Model - * Refactored: fwd_bem_new_model (fwd_bem_model.c) + * @brief Constructs an empty BEM model. * + * All Eigen containers are left at size zero; scalar members are + * set to safe defaults (nsurf = 0, nsol = 0, bem_method = FWD_BEM_UNKNOWN). */ explicit FwdBemModel(); //========================================================================================================= /** - * Destroys the BEM Model - * Refactored: fwd_bem_free_model (fwd_bem_free_model.c) + * @brief Destroys the BEM model. + * + * Surface ownership is released automatically via std::unique_ptr. + * The solution matrix is freed by fwd_bem_free_solution(). */ virtual ~FwdBemModel(); //========================================================================================================= /** - * Refactored: fwd_bem_free_solution (fwd_bem_model.c) + * @brief Release the potential solution matrix and associated workspace. + * + * Resets solution, v0, sol_name, nsol, and bem_method to their + * default (empty / unknown) state. */ void fwd_bem_free_solution(); -//char *fwd_bem_make_bem_name(char *name) -///* -// * Make a standard BEM file name -// */ -//{ -// char *s1,*s2; - -// s1 = strip_from(name,(char*)(".fif")); -// s2 = strip_from(s1,(char*)("-sol")); -// FREE_3(s1); -// s1 = strip_from(s2,(char*)("-bem")); -// FREE_3(s2); -// s2 = MALLOC_3(strlen(s1)+strlen(BEM_SUFFIX)+1,char); -// sprintf(s2,"%s%s",s1,BEM_SUFFIX); -// FREE_3(s1); -// return s2; -//} - + /** + * @brief Build a standard BEM solution file name from a model name. + * + * @param[in] name Base BEM file name. + * @return The derived solution file name. + */ static QString fwd_bem_make_bem_sol_name(const QString& name); //============================= fwd_bem_model.c ============================= + /** + * @brief Return a human-readable label for a BEM surface kind. + * + * @param[in] kind FIFF surface ID (e.g. FIFFV_BEM_SURF_ID_BRAIN). + * @return Reference to a static string label. + */ static const QString& fwd_bem_explain_surface(int kind); + /** + * @brief Return a human-readable label for a BEM method. + * + * @param[in] method BEM method constant (FWD_BEM_CONSTANT_COLL / FWD_BEM_LINEAR_COLL). + * @return Reference to a static string label. + */ static const QString& fwd_bem_explain_method(int method); + /** + * @brief Read an integer tag from a FIFF node. + */ static int get_int( FIFFLIB::FiffStream::SPtr& stream, const FIFFLIB::FiffDirNode::SPtr& node,int what,int *res); - /* - * Return a pointer to a specific surface in a BEM + /** + * @brief Find a surface of the given kind in this BEM model. + * + * @param[in] kind Surface ID to look for. + * @return Non-owning pointer to the surface, or nullptr if not found. */ - // Refactored: fwd_bem_find_surface (fwd_bem_model.c) MNELIB::MNESurface* fwd_bem_find_surface(int kind); - static FwdBemModel* fwd_bem_load_surfaces(const QString& name, - int *kinds, - int nkind); + //========================================================================================================= + /** + * @brief Load BEM surfaces of specified kinds from a FIFF file. + * + * @param[in] name Path to the BEM FIFF file. + * @param[in] kinds Surface IDs to load (e.g. FIFFV_BEM_SURF_ID_BRAIN). + * @return BEM model containing the requested surfaces, or nullptr on failure. + */ + static std::unique_ptr fwd_bem_load_surfaces(const QString& name, + const std::vector& kinds); - static FwdBemModel* fwd_bem_load_homog_surface(const QString& name); + //========================================================================================================= + /** + * @brief Load a single-layer (homogeneous) BEM model from a FIFF file. + * + * Convenience wrapper that loads only the inner skull surface. + * + * @param[in] name Path to the BEM FIFF file. + * @return BEM model, or nullptr on failure. + */ + static std::unique_ptr fwd_bem_load_homog_surface(const QString& name); - static FwdBemModel* fwd_bem_load_three_layer_surfaces(const QString& name); + //========================================================================================================= + /** + * @brief Load a three-layer BEM model (scalp, outer skull, inner skull) from a FIFF file. + * + * @param[in] name Path to the BEM FIFF file. + * @return BEM model, or nullptr on failure. + */ + static std::unique_ptr fwd_bem_load_three_layer_surfaces(const QString& name); - static int fwd_bem_load_solution(const QString& name, int bem_method, FwdBemModel* m); + //========================================================================================================= + /** + * @brief Load a pre-computed BEM solution from a FIFF file. + * + * Reads the solution matrix from disk and stores it in this model. + * The solution must match the requested BEM method. + * + * @param[in] name Path to the solution FIFF file. + * @param[in] bem_method Required BEM method (FWD_BEM_CONSTANT_COLL or FWD_BEM_LINEAR_COLL). + * @return OK on success, FAIL on error. + */ + int fwd_bem_load_solution(const QString& name, int bem_method); - static int fwd_bem_set_head_mri_t(FwdBemModel* m, const FIFFLIB::FiffCoordTrans& t); + //========================================================================================================= + /** + * @brief Set the Head-to-MRI coordinate transform for this BEM model. + * + * @param[in] t The head-to-MRI coordinate transformation. + * @return OK on success, FAIL on error. + */ + int fwd_bem_set_head_mri_t(const FIFFLIB::FiffCoordTrans& t); //============================= dipole_fit_guesses.c ============================= - static MNELIB::MNESurface* make_guesses(MNELIB::MNESurface* guess_surf, /* Predefined boundary for the guesses */ - float guessrad, /* Radius for the spherical boundary if the above is missing */ - float *guess_r0, /* Origin for the spherical boundary */ - float grid, /* Spacing between guess points */ - float exclude, /* Exclude points closer than this to the CM of the guess boundary surface */ - float mindist); + //========================================================================================================= + /** + * @brief Generate a set of dipole guess locations inside a boundary surface. + * + * Creates an evenly-spaced grid of candidate source locations, optionally + * excluding points too close to the center of mass or outside the boundary. + * + * @param[in] guess_surf Predefined boundary surface for the guesses (may be nullptr). + * @param[in] guessrad Radius for a spherical boundary if guess_surf is nullptr. + * @param[in] guess_r0 Origin for the spherical boundary. + * @param[in] grid Spacing between guess points (meters). + * @param[in] exclude Exclude points closer than this to the CM of the boundary. + * @param[in] mindist Minimum distance from the boundary surface. + * @return Surface containing the guess locations, or nullptr on failure. + */ + static std::unique_ptr make_guesses(MNELIB::MNESurface* guess_surf, + float guessrad, + const Eigen::Vector3f& guess_r0, + float grid, + float exclude, + float mindist); //============================= fwd_bem_linear_collocation.c ============================= /* * The following approach is based on: * - * de Munck JC: "A linear discretization of the volume conductor boundary integral equation using analytically integrated elements", + * de Munck JC: "A linear discretization of the volume conductor boundary integral equation + * using analytically integrated elements", * IEEE Trans Biomed Eng. 1992 39(9) : 986 - 990 - * */ - static double calc_beta (double *rk,double *rk1); + //========================================================================================================= + /** + * @brief Compute the beta angle used in the linear collocation integration. + * + * @param[in] rk Position vector of vertex k. + * @param[in] rk1 Position vector of vertex k+1. + * @return The beta angle value. + */ + static double calc_beta(const Eigen::Vector3d& rk, const Eigen::Vector3d& rk1); - static void lin_pot_coeff (float *from, /* Origin */ - MNELIB::MNETriangle* to, /* The destination triangle */ - double omega[3]); + //========================================================================================================= + /** + * @brief Compute the linear potential coefficients for one source-destination pair. + * + * @param[in] from Source point. + * @param[in] to Destination triangle. + * @param[out] omega Output coefficients for the three triangle vertices. + */ + static void lin_pot_coeff(const Eigen::Vector3f& from, + MNELIB::MNETriangle& to, + Eigen::Vector3d& omega); - static void correct_auto_elements (MNELIB::MNESurface* surf, - float **mat); + //========================================================================================================= + /** + * @brief Correct the auto (self-coupling) elements of the linear collocation matrix. + * + * @param[in] surf The BEM surface. + * @param[in,out] mat The coefficient matrix to correct (modified in-place). + */ + static void correct_auto_elements(MNELIB::MNESurface& surf, + Eigen::MatrixXf& mat); - static float **fwd_bem_lin_pot_coeff (const QList& surfs); + //========================================================================================================= + /** + * @brief Assemble the full linear-collocation potential coefficient matrix. + * + * @param[in] surfs Vector of BEM surfaces. + * @return Coefficient matrix (rows = nodes, columns = nodes). + */ + static Eigen::MatrixXf fwd_bem_lin_pot_coeff(const std::vector& surfs); - static int fwd_bem_linear_collocation_solution(FwdBemModel* m); + //========================================================================================================= + /** + * @brief Compute the linear-collocation BEM solution for this model. + * + * @return OK on success, FAIL on error. + */ + int fwd_bem_linear_collocation_solution(); //============================= fwd_bem_solution.c ============================= - static float **fwd_bem_multi_solution (float **solids, /* The solid-angle matrix */ - float **gamma, /* The conductivity multipliers */ - int nsurf, /* Number of surfaces */ - int *ntri); + //========================================================================================================= + /** + * @brief Compute the multi-surface BEM solution from solid-angle coefficients. + * + * Applies the deflation technique and LU decomposition to produce + * the final BEM solution matrix for a multi-compartment model. + * + * @param[in] solids Solid-angle coefficient matrix. + * @param[in] gamma Conductivity-ratio coupling matrix (nullptr for homogeneous). + * @param[in] nsurf Number of surfaces. + * @param[in] ntri Triangle or node count per surface. + * @return Solution matrix, or empty matrix on error. + */ + static Eigen::MatrixXf fwd_bem_multi_solution(Eigen::MatrixXf& solids, + const Eigen::MatrixXf *gamma, + int nsurf, + const Eigen::VectorXi& ntri); - static float **fwd_bem_homog_solution (float **solids,int ntri); + //========================================================================================================= + /** + * @brief Compute the homogeneous (single-layer) BEM solution. + * + * @param[in] solids Solid-angle coefficient matrix. + * @param[in] ntri Number of triangles. + * @return Solution matrix, or empty matrix on error. + */ + static Eigen::MatrixXf fwd_bem_homog_solution(Eigen::MatrixXf& solids, int ntri); - static void fwd_bem_ip_modify_solution(float **solution, /* The original solution */ - float **ip_solution, /* The isolated problem solution */ - float ip_mult, /* Conductivity ratio */ - int nsurf, /* Number of surfaces */ - int *ntri); + //========================================================================================================= + /** + * @brief Modify the BEM solution with the isolated-problem (IP) approach. + * + * Applies the correction for the innermost surface conductivity jump + * (the "isolated problem" approach of Hamalainen and Sarvas, 1989). + * + * @param[in,out] solution The original solution matrix (modified in-place). + * @param[in] ip_solution The isolated-problem solution matrix. + * @param[in] ip_mult Conductivity ratio for the IP correction. + * @param[in] nsurf Number of surfaces. + * @param[in] ntri Triangle or node count per surface. + */ + static void fwd_bem_ip_modify_solution(Eigen::MatrixXf &solution, + Eigen::MatrixXf& ip_solution, + float ip_mult, + int nsurf, + const Eigen::VectorXi &ntri); //============================= fwd_bem_constant_collocation.c ============================= - static int fwd_bem_check_solids (float **angles,int ntri1,int ntri2, float desired); + //========================================================================================================= + /** + * @brief Verify that solid-angle sums match the expected value. + * + * @param[in] angles Solid-angle matrix. + * @param[in] ntri1 Row count. + * @param[in] ntri2 Column count. + * @param[in] desired Expected solid-angle sum per row. + * @return OK if all rows match within tolerance, FAIL otherwise. + */ + static int fwd_bem_check_solids(const Eigen::MatrixXf& angles, int ntri1, int ntri2, float desired); - static float **fwd_bem_solid_angles (const QList& surfs); + //========================================================================================================= + /** + * @brief Compute the solid-angle matrix for all BEM surfaces. + * + * @param[in] surfs Vector of BEM surfaces. + * @return Solid-angle matrix, or empty matrix on error. + */ + static Eigen::MatrixXf fwd_bem_solid_angles(const std::vector& surfs); - static int fwd_bem_constant_collocation_solution(FwdBemModel* m); + //========================================================================================================= + /** + * @brief Compute the constant-collocation BEM solution for this model. + * + * @return OK on success, FAIL on error. + */ + int fwd_bem_constant_collocation_solution(); //============================= fwd_bem_model.c ============================= - static int fwd_bem_compute_solution(FwdBemModel* m, - int bem_method); + //========================================================================================================= + /** + * @brief Compute the BEM solution matrix using the specified method. + * + * @param[in] bem_method BEM method (FWD_BEM_CONSTANT_COLL or FWD_BEM_LINEAR_COLL). + * @return OK on success, FAIL on error. + */ + int fwd_bem_compute_solution(int bem_method); - static int fwd_bem_load_recompute_solution(const QString& name, - int bem_method, - int force_recompute, - FwdBemModel* m); + //========================================================================================================= + /** + * @brief Load a BEM solution from file, recomputing if necessary. + * + * Attempts to read the pre-computed solution; if it is missing or + * force_recompute is set, computes a fresh solution. + * + * @param[in] name Path to the BEM model file. + * @param[in] bem_method Required BEM method. + * @param[in] force_recompute If non-zero, always recompute the solution. + * @return OK on success, FAIL on error. + */ + int fwd_bem_load_recompute_solution(const QString& name, + int bem_method, + int force_recompute); //============================= fwd_bem_pot.c ============================= - static float fwd_bem_inf_field(float *rd, /* Dipole position */ - float *Q, /* Dipole moment */ - float *rp, /* Field point */ - float *dir); - - static float fwd_bem_inf_pot (float *rd, /* Dipole position */ - float *Q, /* Dipole moment */ - float *rp); - - static int fwd_bem_specify_els(FwdBemModel* m, - FwdCoilSet* els); - - static void fwd_bem_pot_grad_calc (float *rd, /* Dipole position */ - float *Q, /* Dipole orientation */ - FwdBemModel* m, /* The model */ - FwdCoilSet* els, /* Use this electrode set if available */ - int all_surfs, /* Compute solution on all surfaces? */ - float *xgrad, - float *ygrad, - float *zgrad); - - static void fwd_bem_lin_pot_calc (float *rd, /* Dipole position */ - float *Q, /* Dipole orientation */ - FwdBemModel* m, /* The model */ - FwdCoilSet* els, /* Use this electrode set if available */ - int all_surfs, /* Compute on all surfaces? */ - float *pot); - - static void fwd_bem_lin_pot_grad_calc (float *rd, /* Dipole position */ - float *Q, /* Dipole orientation */ - FwdBemModel* m, /* The model */ - FwdCoilSet* els, /* Use this electrode set if available */ - int all_surfs, /* Compute on all surfaces? */ - float *xgrad, - float *ygrad, - float *zgrad); - - static void fwd_bem_pot_calc (float *rd, /* Dipole position */ - float *Q, /* Dipole orientation */ - FwdBemModel* m, /* The model */ - FwdCoilSet* els, /* Use this electrode set if available */ - int all_surfs, /* Compute solution on all surfaces? */ - float *pot); - - static int fwd_bem_pot_els (float *rd, /* Dipole position */ - float *Q, /* Dipole orientation */ - FwdCoilSet* els, /* Electrode descriptors */ - float *pot, /* Result */ - void *client); - - static int fwd_bem_pot_grad_els (float *rd, /* Dipole position */ - float *Q, /* Dipole orientation */ - FwdCoilSet* els, /* Electrode descriptors */ - float *pot, /* Potentials */ - float *xgrad, /* Derivatives with respect to dipole position */ - float *ygrad, - float *zgrad, - void *client); + //========================================================================================================= + /** + * @brief Compute the infinite-medium magnetic field at a single point. + * + * Returns the component of the magnetic field along the given direction, + * without the mu_0 / (4 pi) prefactor. + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole moment. + * @param[in] rp Field point. + * @param[in] dir Field direction of interest (unit vector). + * @return The infinite-medium magnetic field component. + */ + static float fwd_bem_inf_field(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, const Eigen::Vector3f& rp, const Eigen::Vector3f& dir); + + //========================================================================================================= + /** + * @brief Compute the infinite-medium electric potential at a single point. + * + * Returns the potential without the 1 / (4 pi sigma) prefactor. + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole moment. + * @param[in] rp Potential evaluation point. + * @return The infinite-medium electric potential. + */ + static float fwd_bem_inf_pot(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, const Eigen::Vector3f& rp); + + //========================================================================================================= + /** + * @brief Precompute the electrode-specific BEM solution. + * + * Builds the coil-specific solution matrix for EEG electrodes by + * multiplying the BEM solution with infinite-medium potentials. + * + * @param[in] els Electrode (coil) set to prepare. + * @return OK on success, FAIL on error. + */ + int fwd_bem_specify_els(FwdCoilSet* els); + + //========================================================================================================= + /** + * @brief Compute the gradient of BEM potentials with respect to dipole position (constant collocation). + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole orientation. + * @param[in] els Electrode set. + * @param[in] all_surfs If non-zero, compute on all surfaces. + * @param[out] xgrad Gradient with respect to x (one value per electrode). + * @param[out] ygrad Gradient with respect to y. + * @param[out] zgrad Gradient with respect to z. + */ + void fwd_bem_pot_grad_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet* els, int all_surfs, + Eigen::VectorXf& xgrad, Eigen::VectorXf& ygrad, Eigen::VectorXf& zgrad); + + //========================================================================================================= + /** + * @brief Compute BEM potentials at electrodes using linear collocation. + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole orientation. + * @param[in] els Electrode set. + * @param[in] all_surfs If non-zero, compute on all surfaces. + * @param[out] pot Output potentials (one value per electrode). + */ + void fwd_bem_lin_pot_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet* els, int all_surfs, + Eigen::VectorXf& pot); + + //========================================================================================================= + /** + * @brief Compute the gradient of BEM potentials with respect to dipole position (linear collocation). + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole orientation. + * @param[in] els Electrode set. + * @param[in] all_surfs If non-zero, compute on all surfaces. + * @param[out] xgrad Gradient with respect to x (one value per electrode). + * @param[out] ygrad Gradient with respect to y. + * @param[out] zgrad Gradient with respect to z. + */ + void fwd_bem_lin_pot_grad_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet* els, int all_surfs, + Eigen::VectorXf& xgrad, Eigen::VectorXf& ygrad, Eigen::VectorXf& zgrad); + + //========================================================================================================= + /** + * @brief Compute BEM potentials at electrodes using constant collocation. + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole orientation. + * @param[in] els Electrode set. + * @param[in] all_surfs If non-zero, compute on all surfaces. + * @param[out] pot Output potentials (one value per electrode). + */ + void fwd_bem_pot_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet* els, int all_surfs, + Eigen::VectorXf& pot); + + //========================================================================================================= + /** + * @brief Callback: compute BEM potentials at electrodes for a dipole. + * + * Matches the fwdFieldFunc signature for use in forward computation threads. + * + * @param[in] rd Dipole position (3-element array). + * @param[in] Q Dipole orientation (3-element array). + * @param[in] els Electrode descriptors. + * @param[out] pot Output potentials. + * @param[in] client Opaque pointer to the FwdBemModel instance. + * @return OK on success, FAIL on error. + */ + static int fwd_bem_pot_els(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet* els, float *pot, + void *client); + + //========================================================================================================= + /** + * @brief Callback: compute BEM potentials and position gradients at electrodes. + * + * Matches the fwdFieldGradFunc signature for use in forward computation threads. + * + * @param[in] rd Dipole position (3-element array). + * @param[in] Q Dipole orientation (3-element array). + * @param[in] els Electrode descriptors. + * @param[out] pot Output potentials. + * @param[out] xgrad Gradient with respect to x. + * @param[out] ygrad Gradient with respect to y. + * @param[out] zgrad Gradient with respect to z. + * @param[in] client Opaque pointer to the FwdBemModel instance. + * @return OK on success, FAIL on error. + */ + static int fwd_bem_pot_grad_els(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet* els, float *pot, + float *xgrad, float *ygrad, float *zgrad, + void *client); //============================= fwd_bem_field.c ============================= /* - * These are some of the integration formulas listed in - * - * L. Urankar, Common compact analytical formulas for computation of + * Integration formulas from: + * L. Urankar, "Common compact analytical formulas for computation of * geometry integrals on a basic Cartesian sub-domain in boundary and - * volume integral methods, Engineering Analysis with Boundary Elements, - * 7 (3), 1990, 124 - 129. - * + * volume integral methods", Engineering Analysis with Boundary Elements, + * 7(3), 1990, 124-129. */ - static void calc_f (double *xx, - double *yy, /* Corner coordinates */ - double *f0, - double *fx, - double *fy); + //========================================================================================================= + /** + * @brief Compute the f0, fx, fy integration helper values from corner coordinates. + * + * @param[in] xx Corner x-coordinates. + * @param[in] yy Corner y-coordinates. + * @param[out] f0 Integral f0. + * @param[out] fx Integral fx. + * @param[out] fy Integral fy. + */ + static void calc_f(const Eigen::Vector3d& xx, const Eigen::Vector3d& yy, + Eigen::Vector3d& f0, Eigen::Vector3d& fx, Eigen::Vector3d& fy); - static void calc_magic (double u,double z, - double A, - double B, - double *beta, - double *D); + //========================================================================================================= + /** + * @brief Compute the "magic" beta and D factors for the Urankar field integration. + * + * @param[in] u Coordinate u. + * @param[in] z Coordinate z. + * @param[in] A Parameter A. + * @param[in] B Parameter B. + * @param[out] beta Output beta factor. + * @param[out] D Output D factor. + */ + static void calc_magic(double u, double z, + double A, double B, + Eigen::Vector3d& beta, double& D); - static void field_integrals (float *from, - MNELIB::MNETriangle* to, - double *I1p, - double *T,double *S1,double *S2, - double *f0,double *fx,double *fy); + //========================================================================================================= + /** + * @brief Compute the geometry integrals for the magnetic field from a triangle. + * + * @param[in] from Source point. + * @param[in] to Destination triangle. + * @param[out] I1p Monopolar integral. + * @param[out] T Integral T. + * @param[out] S1 Integral S1. + * @param[out] S2 Integral S2. + * @param[out] f0 Integral f0. + * @param[out] fx Integral fx. + * @param[out] fy Integral fy. + */ + static void field_integrals(const Eigen::Vector3f& from, + MNELIB::MNETriangle& to, + double& I1p, + Eigen::Vector2d& T, Eigen::Vector2d& S1, Eigen::Vector2d& S2, + Eigen::Vector3d& f0, Eigen::Vector3d& fx, Eigen::Vector3d& fy); - static double one_field_coeff (float *dest, /* The destination field point */ - float *normal, /* The field direction we are interested in */ - MNELIB::MNETriangle* tri); + //========================================================================================================= + /** + * @brief Compute the constant-collocation magnetic field coefficient for one triangle. + * + * @param[in] dest Destination field point. + * @param[in] normal Field direction of interest (unit vector). + * @param[in] tri Source triangle. + * @return The field coefficient. + */ + static double one_field_coeff(const Eigen::Vector3f& dest, const Eigen::Vector3f& normal, + MNELIB::MNETriangle& tri); - static float **fwd_bem_field_coeff(FwdBemModel* m, /* The model */ - FwdCoilSet* coils); + //========================================================================================================= + /** + * @brief Assemble the constant-collocation magnetic field coefficient matrix. + * + * @param[in] coils MEG coil set. + * @return Coefficient matrix, or empty matrix on error. + */ + Eigen::MatrixXf fwd_bem_field_coeff(FwdCoilSet* coils); /* - * These are the formulas from Ferguson et al - * A Complete Linear Discretization for Calculating the Magnetic Field - * Using the Boundary-Element Method, IEEE Trans. Biomed. Eng., submitted - */ - static double calc_gamma (double *rk,double *rk1); - - static void fwd_bem_one_lin_field_coeff_ferg (float *dest, /* The field point */ - float *dir, /* The interesting direction */ - MNELIB::MNETriangle* tri, /* The destination triangle */ - double *res); - - static void fwd_bem_one_lin_field_coeff_uran(float *dest, /* The field point */ - float *dir, /* The interesting direction */ - MNELIB::MNETriangle* tri, /* The destination triangle */ - double *res); - - static void fwd_bem_one_lin_field_coeff_simple (float *dest, /* The destination field point */ - float *normal, /* The field direction we are interested in */ - MNELIB::MNETriangle* source, /* The source triangle */ - double *res); - - typedef void (* linFieldIntFunc)(float *dest,float *dir,MNELIB::MNETriangle* tri, double *res); - - static float **fwd_bem_lin_field_coeff (FwdBemModel* m, /* The model */ - FwdCoilSet* coils, /* Coil information */ - int method); - - static int fwd_bem_specify_coils(FwdBemModel* m, - FwdCoilSet* coils); - - #define MAG_FACTOR 1e-7 /* \mu_0/4\pi */ - - static void fwd_bem_lin_field_calc(float *rd, - float *Q, - FwdCoilSet* coils, - FwdBemModel* m, - float *B); - - static void fwd_bem_field_calc(float *rd, - float *Q, - FwdCoilSet* coils, - FwdBemModel* m, - float *B); - - static void fwd_bem_field_grad_calc(float *rd, - float *Q, - FwdCoilSet *coils, - FwdBemModel *m, - float *xgrad, - float *ygrad, - float *zgrad); - - static float fwd_bem_inf_field_der(float *rd, /* Dipole position */ - float *Q, /* Dipole moment */ - float *rp, /* Field point */ - float *dir, /* Which field component */ - float *comp); - - static float fwd_bem_inf_pot_der (float *rd, /* Dipole position */ - float *Q, /* Dipole moment */ - float *rp, /* Potential point */ - float *comp); - - static void fwd_bem_lin_field_grad_calc(float *rd, - float *Q, - FwdCoilSet *coils, - FwdBemModel *m, - float *xgrad, - float *ygrad, - float *zgrad); - - static int fwd_bem_field(float *rd, /* Dipole position */ - float *Q, /* Dipole orientation */ - FwdCoilSet* coils, /* Coil descriptors */ - float *B, /* Result */ - void *client); - - static int fwd_bem_field_grad(float *rd, /* The dipole location */ - float Q[], /* The dipole components (xyz) */ - FwdCoilSet* coils, /* The coil definitions */ - float Bval[], /* Results */ - float xgrad[], /* The derivatives with respect to */ - float ygrad[], /* the dipole position coordinates */ - float zgrad[], - void *client); + * Linear field formulas from: + * Ferguson et al., "A Complete Linear Discretization for Calculating + * the Magnetic Field Using the Boundary-Element Method", + * IEEE Trans. Biomed. Eng., submitted. + */ + + //========================================================================================================= + /** + * @brief Compute the gamma angle for the linear field integration (Ferguson). + * + * @param[in] rk Position vector of vertex k. + * @param[in] rk1 Position vector of vertex k+1. + * @return The gamma angle value. + */ + static double calc_gamma(const Eigen::Vector3d& rk, const Eigen::Vector3d& rk1); + + //========================================================================================================= + /** + * @brief Compute linear field coefficients using the Ferguson method. + * + * @param[in] dest Field point. + * @param[in] dir Field direction of interest (unit vector). + * @param[in] tri Destination triangle. + * @param[out] res Output coefficients for the three triangle vertices. + */ + static void fwd_bem_one_lin_field_coeff_ferg(const Eigen::Vector3f& dest, const Eigen::Vector3f& dir, + MNELIB::MNETriangle& tri, + Eigen::Vector3d& res); + + //========================================================================================================= + /** + * @brief Compute linear field coefficients using the Urankar method. + * + * @param[in] dest Field point. + * @param[in] dir Field direction of interest (unit vector). + * @param[in] tri Destination triangle. + * @param[out] res Output coefficients for the three triangle vertices. + */ + static void fwd_bem_one_lin_field_coeff_uran(const Eigen::Vector3f& dest, const Eigen::Vector3f& dir, + MNELIB::MNETriangle& tri, + Eigen::Vector3d& res); + + //========================================================================================================= + /** + * @brief Compute linear field coefficients using the simple (direct) method. + * + * @param[in] dest Destination field point. + * @param[in] normal Field direction of interest (unit vector). + * @param[in] source Source triangle. + * @param[out] res Output coefficients for the three triangle vertices. + */ + static void fwd_bem_one_lin_field_coeff_simple(const Eigen::Vector3f& dest, const Eigen::Vector3f& normal, + MNELIB::MNETriangle& source, + Eigen::Vector3d& res); + + //========================================================================================================= + /** + * @brief Function pointer type for linear field coefficient integration methods. + */ + typedef void (*linFieldIntFunc)(const Eigen::Vector3f& dest, const Eigen::Vector3f& dir, + MNELIB::MNETriangle& tri, Eigen::Vector3d& res); + + //========================================================================================================= + /** + * @brief Assemble the linear-collocation magnetic field coefficient matrix. + * + * @param[in] coils MEG coil set. + * @param[in] method Integration method (FWD_BEM_LIN_FIELD_SIMPLE, _FERGUSON, or _URANKAR). + * @return Coefficient matrix, or empty matrix on error. + */ + Eigen::MatrixXf fwd_bem_lin_field_coeff(FwdCoilSet* coils, int method); + + //========================================================================================================= + /** + * @brief Precompute the coil-specific BEM solution for MEG. + * + * Builds the coil-specific solution matrix by multiplying the BEM + * solution with the field coefficients. + * + * @param[in] coils MEG coil set to prepare. + * @return OK on success, FAIL on error. + */ + int fwd_bem_specify_coils(FwdCoilSet* coils); + + #define MAG_FACTOR 1e-7 /**< Magnetic constant mu_0 / (4 * pi). */ + + //========================================================================================================= + /** + * @brief Compute BEM magnetic fields at coils using linear collocation. + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole orientation. + * @param[in] coils MEG coil set. + * @param[out] B Output magnetic fields (one value per coil). + */ + void fwd_bem_lin_field_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet& coils, Eigen::VectorXf& B); + + //========================================================================================================= + /** + * @brief Compute BEM magnetic fields at coils using constant collocation. + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole orientation. + * @param[in] coils MEG coil set. + * @param[out] B Output magnetic fields (one value per coil). + */ + void fwd_bem_field_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet& coils, Eigen::VectorXf& B); + + //========================================================================================================= + /** + * @brief Compute the gradient of BEM magnetic fields with respect to dipole position (constant collocation). + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole orientation. + * @param[in] coils MEG coil set. + * @param[out] xgrad Gradient with respect to x (one value per coil). + * @param[out] ygrad Gradient with respect to y. + * @param[out] zgrad Gradient with respect to z. + */ + void fwd_bem_field_grad_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet& coils, + Eigen::VectorXf& xgrad, Eigen::VectorXf& ygrad, Eigen::VectorXf& zgrad); + + //========================================================================================================= + /** + * @brief Compute the derivative of the infinite-medium magnetic field with respect to dipole position. + * + * Returns the field derivative without the mu_0 / (4 pi) prefactor. + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole moment. + * @param[in] rp Field point. + * @param[in] dir Field direction of interest (unit vector). + * @param[in] comp Gradient component direction (unit vector). + * @return The field derivative value. + */ + static float fwd_bem_inf_field_der(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, const Eigen::Vector3f& rp, + const Eigen::Vector3f& dir, const Eigen::Vector3f& comp); + + //========================================================================================================= + /** + * @brief Compute the derivative of the infinite-medium electric potential with respect to dipole position. + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole moment. + * @param[in] rp Potential evaluation point. + * @param[in] comp Gradient component direction (unit vector). + * @return The potential derivative value. + */ + static float fwd_bem_inf_pot_der(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, const Eigen::Vector3f& rp, + const Eigen::Vector3f& comp); + + //========================================================================================================= + /** + * @brief Compute the gradient of BEM magnetic fields with respect to dipole position (linear collocation). + * + * @param[in] rd Dipole position. + * @param[in] Q Dipole orientation. + * @param[in] coils MEG coil set. + * @param[out] xgrad Gradient with respect to x (one value per coil). + * @param[out] ygrad Gradient with respect to y. + * @param[out] zgrad Gradient with respect to z. + */ + void fwd_bem_lin_field_grad_calc(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet& coils, + Eigen::VectorXf& xgrad, Eigen::VectorXf& ygrad, Eigen::VectorXf& zgrad); + + //========================================================================================================= + /** + * @brief Callback: compute BEM magnetic fields at coils for a dipole. + * + * Dispatches to fwd_bem_field_calc or fwd_bem_lin_field_calc based on + * the current BEM method. Matches the fwdFieldFunc signature. + * + * @param[in] rd Dipole position (3-element array). + * @param[in] Q Dipole orientation (3-element array). + * @param[in] coils MEG coil descriptors. + * @param[out] B Output magnetic fields. + * @param[in] client Opaque pointer to the FwdBemModel instance. + * @return OK on success, FAIL on error. + */ + static int fwd_bem_field(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet* coils, float *B, + void *client); + + //========================================================================================================= + /** + * @brief Callback: compute BEM magnetic fields and position gradients at coils. + * + * Matches the fwdFieldGradFunc signature for use in forward computation threads. + * + * @param[in] rd Dipole position (3-element array). + * @param[in] Q Dipole orientation (3-element array). + * @param[in] coils MEG coil definitions. + * @param[out] Bval Output magnetic fields. + * @param[out] xgrad Gradient with respect to x. + * @param[out] ygrad Gradient with respect to y. + * @param[out] zgrad Gradient with respect to z. + * @param[in] client Opaque pointer to the FwdBemModel instance. + * @return OK on success, FAIL on error. + */ + static int fwd_bem_field_grad(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet* coils, float Bval[], + float xgrad[], float ygrad[], float zgrad[], + void *client); //============================= compute_forward.c ============================= - static void *meg_eeg_fwd_one_source_space(void *arg); - - // TODO check if this is the correct class or move - static int compute_forward_meg( std::vector>& spaces, /**< Source spaces. */ - FwdCoilSet* coils, /**< MEG Coilset. */ - FwdCoilSet* comp_coils, /**< Compensator Coilset. */ - MNELIB::MNECTFCompDataSet* comp_data, /**< Compensator Data. */ - bool fixed_ori, /**< Use fixed-orientation dipoles. */ - FwdBemModel* bem_model, /**< BEM model definition. */ - Eigen::Vector3f* r0, /**< Sphere model origin. */ - bool use_threads, /**< Parallelize with threads?. */ - FIFFLIB::FiffNamedMatrix& resp, /**< The results. */ - FIFFLIB::FiffNamedMatrix& resp_grad, - bool bDoGRad); /**< calculate gradient solution. */ - - static int compute_forward_eeg( std::vector>& spaces, /**< Source spaces. */ - FwdCoilSet* els, /**< Electrode locations. */ - bool fixed_ori, /**< Use fixed-orientation dipoles. */ - FwdBemModel* bem_model, /**< BEM model definition. */ - FwdEegSphereModel* m, /**< Sphere model definition. */ - bool use_threads, /**< Parallelize with threads?. */ - FIFFLIB::FiffNamedMatrix& resp, /**< The results. */ - FIFFLIB::FiffNamedMatrix& resp_grad, - bool bDoGrad); /**< calculate gradient solution. */ + //========================================================================================================= + /** + * @brief Thread worker: compute the forward solution for one source space. + * + * Used as a thread entry point; the argument is cast to the appropriate + * worker struct internally. + * + * @param[in] arg Per-thread work descriptor. + */ + static void meg_eeg_fwd_one_source_space(FwdThreadArg* arg); + + //========================================================================================================= + /** + * @brief Compute the MEG forward solution for one or more source spaces. + * + * @param[in] spaces Source spaces. + * @param[in] coils MEG coil set. + * @param[in] comp_coils Compensator coil set (may be nullptr). + * @param[in] comp_data CTF compensation data (may be nullptr). + * @param[in] fixed_ori If true, use fixed-orientation dipoles. + * @param[in] r0 Sphere model origin. + * @param[in] use_threads If true, parallelize across source spaces. + * @param[out] resp Forward solution matrix. + * @param[out] resp_grad Gradient forward solution matrix. + * @param[in] bDoGRad If true, also compute the gradient solution. + * @return OK on success, FAIL on error. + */ + int compute_forward_meg(std::vector>& spaces, + FwdCoilSet* coils, + FwdCoilSet* comp_coils, + MNELIB::MNECTFCompDataSet* comp_data, + bool fixed_ori, + const Eigen::Vector3f& r0, + bool use_threads, + FIFFLIB::FiffNamedMatrix& resp, + FIFFLIB::FiffNamedMatrix& resp_grad, + bool bDoGRad); + + //========================================================================================================= + /** + * @brief Compute the EEG forward solution for one or more source spaces. + * + * @param[in] spaces Source spaces. + * @param[in] els Electrode locations. + * @param[in] fixed_ori If true, use fixed-orientation dipoles. + * @param[in] eeg_model Sphere model definition. + * @param[in] use_threads If true, parallelize across source spaces. + * @param[out] resp Forward solution matrix. + * @param[out] resp_grad Gradient forward solution matrix. + * @param[in] bDoGrad If true, also compute the gradient solution. + * @return OK on success, FAIL on error. + */ + int compute_forward_eeg(std::vector>& spaces, + FwdCoilSet* els, + bool fixed_ori, + FwdEegSphereModel* eeg_model, + bool use_threads, + FIFFLIB::FiffNamedMatrix& resp, + FIFFLIB::FiffNamedMatrix& resp_grad, + bool bDoGrad); //============================= fwd_spherefield.c ============================= - // TODO location of these functions need to be checked -> evtl moving to more suitable space - static int fwd_sphere_field(float *rd, /* The dipole location */ - float Q[], /* The dipole components (xyz) */ - FwdCoilSet* coils, /* The coil definitions */ - float Bval[], /* Results */ - void *client); - - static int fwd_sphere_field_vec(float *rd, /* The dipole location */ - FwdCoilSet* coils, /* The coil definitions */ - float **Bval, /* Results: rows are the fields of the x,y, and z direction dipoles */ - void *client); - - static int fwd_sphere_field_grad(float *rd, /* The dipole location */ - float Q[], /* The dipole components (xyz) */ - FwdCoilSet* coils, /* The coil definitions */ - float Bval[], /* Results */ - float xgrad[], /* The derivatives with respect to */ - float ygrad[], /* the dipole position coordinates */ - float zgrad[], - void *client); + + //========================================================================================================= + /** + * @brief Callback: compute the spherical-model magnetic field at coils. + * + * Matches the fwdFieldFunc signature. + * + * @param[in] rd Dipole position (3-element array). + * @param[in] Q Dipole components (xyz). + * @param[in] coils MEG coil definitions. + * @param[out] Bval Output magnetic fields. + * @param[in] client Opaque pointer to client data (sphere model origin). + * @return OK on success, FAIL on error. + */ + static int fwd_sphere_field(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet* coils, float Bval[], + void *client); + + //========================================================================================================= + /** + * @brief Callback: compute the spherical-model vector magnetic field at coils. + * + * Returns one row per cardinal dipole direction (x, y, z). + * Matches the fwdVecFieldFunc signature. + * + * @param[in] rd Dipole position (3-element array). + * @param[in] coils MEG coil definitions. + * @param[out] Bval Output fields (3 x ncoil matrix, row-major). + * @param[in] client Opaque pointer to client data. + * @return OK on success, FAIL on error. + */ + static int fwd_sphere_field_vec(const Eigen::Vector3f& rd, + FwdCoilSet* coils, float **Bval, + void *client); + + //========================================================================================================= + /** + * @brief Callback: compute the spherical-model magnetic field and its position gradient at coils. + * + * Matches the fwdFieldGradFunc signature. + * + * @param[in] rd Dipole position (3-element array). + * @param[in] Q Dipole components (xyz). + * @param[in] coils MEG coil definitions. + * @param[out] Bval Output magnetic fields. + * @param[out] xgrad Gradient with respect to x. + * @param[out] ygrad Gradient with respect to y. + * @param[out] zgrad Gradient with respect to z. + * @param[in] client Opaque pointer to client data. + * @return OK on success, FAIL on error. + */ + static int fwd_sphere_field_grad(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, + FwdCoilSet* coils, float Bval[], + float xgrad[], float ygrad[], float zgrad[], + void *client); //============================= fwd_mag_dipole_field.c ============================= - // TODO location of these functions need to be checked -> evtl moving to mor suitable space - /* - * Compute the field of a magnetic dipole + + //========================================================================================================= + /** + * @brief Callback: compute the magnetic field of a magnetic dipole at coils. + * + * @param[in] rm Dipole position (in the same coordinate system as the coils). + * @param[in] M Dipole moment components (xyz). + * @param[in] coils MEG coil definitions. + * @param[out] Bval Output magnetic fields. + * @param[in] client Opaque pointer to client data (unused, may be nullptr). + * @return OK on success, FAIL on error. */ - static int fwd_mag_dipole_field( float *rm, /* The dipole location in the same coordinate system as the coils */ - float M[], /* The dipole components (xyz) */ - FwdCoilSet* coils, /* The coil definitions */ - float Bval[], /* Results */ - void *client); + static int fwd_mag_dipole_field(const Eigen::Vector3f& rm, const Eigen::Vector3f& M, + FwdCoilSet* coils, float Bval[], + void *client); - static int fwd_mag_dipole_field_vec( float *rm, /* The dipole location */ - FwdCoilSet* coils, /* The coil definitions */ - float **Bval, /* Results: rows are the fields of the x,y, and z direction dipoles */ - void *client); + //========================================================================================================= + /** + * @brief Callback: compute the vector magnetic field of a magnetic dipole at coils. + * + * Returns one row per cardinal dipole direction (x, y, z). + * + * @param[in] rm Dipole position (3-element array). + * @param[in] coils MEG coil definitions. + * @param[out] Bval Output fields (3 x ncoil matrix, row-major). + * @param[in] client Opaque pointer to client data (unused, may be nullptr). + * @return OK on success, FAIL on error. + */ + static int fwd_mag_dipole_field_vec(const Eigen::Vector3f& rm, + FwdCoilSet* coils, float **Bval, + void *client); public: - QString surf_name; /* Name of the file where surfaces were loaded from */ - QList surfs; /* The interface surfaces from outside towards inside */ - int *ntri; /* Number of triangles on each surface */ - int *np; /* Number of vertices on each surface */ - int nsurf; /* How many */ - float *sigma; /* The conductivities */ - float **gamma; /* The gamma factors */ - float *source_mult; /* These multiply the infinite medium potentials */ - float *field_mult; /* Multipliers for the magnetic field */ - int bem_method; /* Which approximation method is used */ - QString sol_name; /* Name of the file where the solution was loaded from */ - - float **solution; /* The potential solution matrix */ - float *v0; /* Space for the infinite-medium potentials */ - int nsol; /* Size of the solution matrix */ - - FIFFLIB::FiffCoordTrans head_mri_t; /* Coordinate transformation from head to MRI coordinates */ - - float ip_approach_limit; /* Controls whether we need to use the isolated problem approach */ - bool use_ip_approach; /* Do we need it */ - -// ### OLD STRUCT ### -//typedef struct { -// char *surf_name; /* Name of the file where surfaces were loaded from */ -// FWDLIB::MNESurface* *surfs; /* The interface surfaces from outside towards inside */ -// int *ntri; /* Number of triangles on each surface */ -// int *np; /* Number of vertices on each surface */ -// int nsurf; /* How many */ -// float *sigma; /* The conductivities */ -// float **gamma; /* The gamma factors */ -// float *source_mult; /* These multiply the infinite medium potentials */ -// float *field_mult; /* Multipliers for the magnetic field */ -// int bem_method; /* Which approximation method is used */ -// char *sol_name; /* Name of the file where the solution was loaded from */ - -// float **solution; /* The potential solution matrix */ -// float *v0; /* Space for the infinite-medium potentials */ -// int nsol; /* Size of the solution matrix */ - -// FWDLIB::FiffCoordTransOld* head_mri_t; /* Coordinate transformation from head to MRI coordinates */ - -// float ip_approach_limit; /* Controls whether we need to use the isolated problem approach */ -// int use_ip_approach; /* Do we need it */ -//} *FwdBemModel*,FwdBemModel*Rec; /* Holds the BEM model definition */ + QString surf_name; /**< File from which surfaces were loaded. */ + + std::vector> surfs; /**< Interface surfaces, outermost first. */ + + Eigen::VectorXi ntri; /**< Triangle count per surface (length nsurf). */ + Eigen::VectorXi np; /**< Vertex count per surface (length nsurf). */ + int nsurf; /**< Number of interface surfaces. */ + + Eigen::VectorXf sigma; /**< Conductivity of each layer (length nsurf). */ + Eigen::MatrixXf gamma; /**< Conductivity-ratio coupling matrix (nsurf x nsurf). */ + Eigen::VectorXf source_mult; /**< Infinite-medium potential multipliers (length nsurf). */ + Eigen::VectorXf field_mult; /**< Magnetic-field multipliers (length nsurf). */ + + int bem_method; /**< Approximation method (FWD_BEM_CONSTANT_COLL or FWD_BEM_LINEAR_COLL). */ + QString sol_name; /**< File from which the solution was loaded. */ + + Eigen::MatrixXf solution; /**< Potential solution matrix (nsol x nsol). */ + Eigen::VectorXf v0; /**< Workspace for infinite-medium potentials (length nsol). */ + int nsol; /**< Dimension of the solution matrix. */ + + FIFFLIB::FiffCoordTrans head_mri_t; /**< Head-to-MRI coordinate transform. */ + + float ip_approach_limit; /**< Threshold for isolated-problem approach. */ + bool use_ip_approach; /**< Whether the isolated-problem approach is active. */ }; //============================================================================================================= @@ -550,4 +1045,4 @@ class FWDSHARED_EXPORT FwdBemModel //============================================================================================================= } // NAMESPACE FWDLIB -#endif // FWDBEMMODEL_H +#endif // FWD_BEM_MODEL_H diff --git a/src/libraries/fwd/fwd_bem_solution.cpp b/src/libraries/fwd/fwd_bem_solution.cpp index 3285f9f45c1..ea15054c594 100644 --- a/src/libraries/fwd/fwd_bem_solution.cpp +++ b/src/libraries/fwd/fwd_bem_solution.cpp @@ -40,20 +40,6 @@ #include "fwd_bem_solution.h" -#define MALLOC_42(x,t) (t *)malloc((x)*sizeof(t)) - -#define FREE_42(x) if ((char *)(x) != NULL) free((char *)(x)) - -#define FREE_CMATRIX_42(m) mne_free_cmatrix_42((m)) - -void mne_free_cmatrix_42(float **m) -{ - if (m) { - FREE_42(*m); - FREE_42(m); - } -} - //============================================================================================================= // USED NAMESPACES //============================================================================================================= @@ -66,8 +52,7 @@ using namespace FWDLIB; //============================================================================================================= FwdBemSolution::FwdBemSolution() -:solution(NULL) -,ncoil(0) +:ncoil(0) ,np(0) { } @@ -76,22 +61,4 @@ FwdBemSolution::FwdBemSolution() FwdBemSolution::~FwdBemSolution() { - FREE_CMATRIX_42(solution); -} - -//============================================================================================================= - -void FwdBemSolution::fwd_bem_free_coil_solution(void *user) -{ - FwdBemSolution* sol = (FwdBemSolution*)user; - - if (!sol) - return; - -// FREE_CMATRIX_3(sol->solution); -// FREE_3(sol); - - delete sol; - - return; } diff --git a/src/libraries/fwd/fwd_bem_solution.h b/src/libraries/fwd/fwd_bem_solution.h index e5b1bf5f21c..69524d00832 100644 --- a/src/libraries/fwd/fwd_bem_solution.h +++ b/src/libraries/fwd/fwd_bem_solution.h @@ -30,12 +30,12 @@ * POSSIBILITY OF SUCH DAMAGE. * * - * @brief Forward BEM Solution (FwdBemSolution) class declaration. + * @brief FwdBemSolution class declaration. * */ -#ifndef FWDBEMSOLUTION_H -#define FWDBEMSOLUTION_H +#ifndef FWD_BEM_SOLUTION_H +#define FWD_BEM_SOLUTION_H //============================================================================================================= // INCLUDES @@ -77,33 +77,19 @@ class FWDSHARED_EXPORT FwdBemSolution //========================================================================================================= /** * Constructs the Forward BEM Solution - * Refactored: fwd_bem_new_coil_solution (fwd_bem_model.c) - * */ FwdBemSolution(); //========================================================================================================= /** * Destroys the Forward BEM Solution - * Refactored: fwd_bem_free_coil_solution (fwd_bem_model.c) */ ~FwdBemSolution(); - //============================= fwd_bem_model.c ============================= - //TODO Remove later on use delete instead - static void fwd_bem_free_coil_solution(void *user); - public: - float **solution; /* The solution matrix */ - int ncoil; /* Number of sensors */ - int np; /* Number of potential solution points */ - -// ### OLD STRUCT ### -//typedef struct { /* Space to store a solution matrix */ -// float **solution; /* The solution matrix */ -// int ncoil; /* Number of sensors */ -// int np; /* Number of potential solution points */ -//} *fwdBemSolution,fwdBemSolutionRec; /* Mapping from infinite medium potentials to a particular set of coils or electrodes */ + Eigen::MatrixXf solution; /**< Solution matrix (ncoil x np). */ + int ncoil; /**< Number of sensors. */ + int np; /**< Number of potential solution points. */ }; //============================================================================================================= @@ -111,4 +97,4 @@ class FWDSHARED_EXPORT FwdBemSolution //============================================================================================================= } // NAMESPACE FWDLIB -#endif // FWDBEMSOLUTION_H +#endif // FWD_BEM_SOLUTION_H diff --git a/src/libraries/fwd/fwd_coil.cpp b/src/libraries/fwd/fwd_coil.cpp index beb71fd0815..0e8f1c524eb 100644 --- a/src/libraries/fwd/fwd_coil.cpp +++ b/src/libraries/fwd/fwd_coil.cpp @@ -39,9 +39,9 @@ //============================================================================================================= #include "fwd_coil.h" -//#include #include #include +#include //============================================================================================================= // QT INCLUDES @@ -57,81 +57,7 @@ using namespace Eigen; using namespace FIFFLIB; using namespace FWDLIB; -#define MALLOC_5(x,t) (t *)malloc((x)*sizeof(t)) -#define FREE_5(x) if ((char *)(x) != NULL) free((char *)(x)) -#define FREE_CMATRIX_5(m) mne_free_cmatrix_5((m)) - -#define ALLOC_CMATRIX_5(x,y) mne_cmatrix_5((x),(y)) - -#define X_5 0 -#define Y_5 1 -#define Z_5 2 - -#define VEC_DOT_5(x,y) ((x)[X_5]*(y)[X_5] + (x)[Y_5]*(y)[Y_5] + (x)[Z_5]*(y)[Z_5]) -#define VEC_LEN_5(x) sqrt(VEC_DOT_5(x,x)) - -#define VEC_COPY_5(to,from) {\ - (to)[X_5] = (from)[X_5];\ - (to)[Y_5] = (from)[Y_5];\ - (to)[Z_5] = (from)[Z_5];\ - } - -static void matrix_error_5(int kind, int nr, int nc) - -{ - if (kind == 1) - printf("Failed to allocate memory pointers for a %d x %d matrix\n",nr,nc); - else if (kind == 2) - printf("Failed to allocate memory for a %d x %d matrix\n",nr,nc); - else - printf("Allocation error for a %d x %d matrix\n",nr,nc); - if (sizeof(void *) == 4) { - printf("This is probably because you seem to be using a computer with 32-bit architecture.\n"); - printf("Please consider moving to a 64-bit platform."); - } - printf("Cannot continue. Sorry.\n"); - exit(1); -} - -float **mne_cmatrix_5(int nr,int nc) - -{ - int i; - float **m; - float *whole; - - m = MALLOC_5(nr,float *); - if (!m) matrix_error_5(1,nr,nc); - whole = MALLOC_5(nr*nc,float); - if (!whole) matrix_error_5(2,nr,nc); - - for(i=0;i 0) { - for (k = 0; k < 3; k++) - rr[k] = rr[k]/ll; - } - return; -} //============================================================================================================= // DEFINE MEMBER METHODS @@ -144,9 +70,9 @@ FwdCoil::FwdCoil(int p_np) base = 0.0; size = 0.0; np = p_np; - rmag = ALLOC_CMATRIX_5(np,3); - cosmag = ALLOC_CMATRIX_5(np,3); - w = MALLOC_5(np,float); + rmag = Eigen::Matrix::Zero(np, 3); + cosmag = Eigen::Matrix::Zero(np, 3); + w = Eigen::VectorXf::Zero(np); /* * Reasonable defaults */ @@ -176,20 +102,15 @@ FwdCoil::FwdCoil(const FwdCoil& p_FwdCoil) this->np = p_FwdCoil.np; this->type = p_FwdCoil.type; - rmag = ALLOC_CMATRIX_5(this->np,3); - cosmag = ALLOC_CMATRIX_5(this->np,3); - w = MALLOC_5(this->np,float); + rmag = p_FwdCoil.rmag; + cosmag = p_FwdCoil.cosmag; + w = p_FwdCoil.w; - VEC_COPY_5(this->r0,p_FwdCoil.r0); - VEC_COPY_5(this->ex,p_FwdCoil.ex); - VEC_COPY_5(this->ey,p_FwdCoil.ey); - VEC_COPY_5(this->ez,p_FwdCoil.ez); + std::copy(std::begin(p_FwdCoil.r0), std::end(p_FwdCoil.r0), std::begin(this->r0)); + std::copy(std::begin(p_FwdCoil.ex), std::end(p_FwdCoil.ex), std::begin(this->ex)); + std::copy(std::begin(p_FwdCoil.ey), std::end(p_FwdCoil.ey), std::begin(this->ey)); + std::copy(std::begin(p_FwdCoil.ez), std::end(p_FwdCoil.ez), std::begin(this->ez)); - for (int p = 0; p < p_FwdCoil.np; p++) { - this->w[p] = p_FwdCoil.w[p]; - VEC_COPY_5(this->rmag[p],p_FwdCoil.rmag[p]); - VEC_COPY_5(this->cosmag[p],p_FwdCoil.cosmag[p]); - } this->coord_frame = p_FwdCoil.coord_frame; } @@ -197,9 +118,6 @@ FwdCoil::FwdCoil(const FwdCoil& p_FwdCoil) FwdCoil::~FwdCoil() { - FREE_CMATRIX_5(rmag); - FREE_CMATRIX_5(cosmag); - FREE_5(w); } //============================================================================================================= @@ -228,8 +146,8 @@ FwdCoil *FwdCoil::create_eeg_el(const FiffChInfo& ch, const FiffCoordTrans& t) res->coil_class = FWD_COILC_EEG; res->accuracy = FWD_COIL_ACCURACY_NORMAL; res->type = ch.chpos.coil_type; - VEC_COPY_5(res->r0,ch.chpos.r0); - VEC_COPY_5(res->ex,ch.chpos.ex); + std::copy(ch.chpos.r0.data(), ch.chpos.r0.data() + 3, res->r0); + std::copy(ch.chpos.ex.data(), ch.chpos.ex.data() + 3, res->ex); /* * Optional coordinate transformation */ @@ -244,16 +162,16 @@ FwdCoil *FwdCoil::create_eeg_el(const FiffChInfo& ch, const FiffCoordTrans& t) * The electrode location */ for (c = 0; c < 3; c++) - res->rmag[0][c] = res->cosmag[0][c] = res->r0[c]; - normalize_5(res->cosmag[0]); + res->rmag(0, c) = res->cosmag(0, c) = res->r0[c]; + res->cosmag.row(0).normalize(); res->w[0] = 1.0; /* * Add the reference electrode, if appropriate */ if (res->np == 2) { for (c = 0; c < 3; c++) - res->rmag[1][c] = res->cosmag[1][c] = res->ex[c]; - normalize_5(res->cosmag[1]); + res->rmag(1, c) = res->cosmag(1, c) = res->ex[c]; + res->cosmag.row(1).normalize(); res->w[1] = -1.0; } return res; diff --git a/src/libraries/fwd/fwd_coil.h b/src/libraries/fwd/fwd_coil.h index b88bd3e6709..70ffcddadef 100644 --- a/src/libraries/fwd/fwd_coil.h +++ b/src/libraries/fwd/fwd_coil.h @@ -34,8 +34,8 @@ * */ -#ifndef FWDCOIL_H -#define FWDCOIL_H +#ifndef FWD_COIL_H +#define FWD_COIL_H //============================================================================================================= // INCLUDES @@ -94,14 +94,12 @@ class FWDSHARED_EXPORT FwdCoil //========================================================================================================= /** * Constructs the Forward Coil - * Refactored: fwd_new_coil (fwd_coil_def.c) */ FwdCoil(int p_np); //========================================================================================================= /** * Copy constructor. - * Refactored: fwd_dup_coil (fwd_coil_def.c) * * @param[in] p_FwdCoil FwdCoil which should be copied. */ @@ -110,14 +108,12 @@ class FWDSHARED_EXPORT FwdCoil //========================================================================================================= /** * Destroys the Forward Coil description - * Refactored: fwd_free_coil */ ~FwdCoil(); //========================================================================================================= /** * Create an electrode definition. Transform coordinate frame if so desired. - * Refactored: fwd_create_eeg_el (fwd_coil_def.c) * * @param[in] ch Channel information to use. * @param[in] t Transform the points using this. @@ -130,7 +126,6 @@ class FWDSHARED_EXPORT FwdCoil //========================================================================================================= /** * Checks if this is an axial coil. - * Refactored: fwd_is_axial_coil (fwd_coil_def.c) * * @return True if axial coil, false otherwise. */ @@ -139,7 +134,6 @@ class FWDSHARED_EXPORT FwdCoil //========================================================================================================= /** * Checks if this is an magnetometer. - * Refactored: fwd_is_magnetometer_coil (fwd_coil_def.c) * * @return True if magnetometer, false otherwise. */ @@ -148,7 +142,6 @@ class FWDSHARED_EXPORT FwdCoil //========================================================================================================= /** * Checks if this is an planar coil. - * Refactored: fwd_is_planar_coil (fwd_coil_def.c) * * @return True if planar coil, false otherwise. */ @@ -157,7 +150,6 @@ class FWDSHARED_EXPORT FwdCoil //========================================================================================================= /** * Checks if this is an EEG electrode. - * Refactored: fwd_is_eeg_electrode (fwd_coil_def.c) * * @return True if EEG electrode, false otherwise. */ @@ -177,29 +169,9 @@ class FWDSHARED_EXPORT FwdCoil float ey[3]; /**< This stupid construction needs to be replaced with. */ float ez[3]; /**< a coordinate transformation. */ int np; /**< Number of integration points. */ - float **rmag; /**< The field point locations. */ - float **cosmag; /**< The corresponding direction cosines. */ - float *w; /**< The weighting coefficients. */ - -// ### OLD STRUCT ### -// typedef struct { -// char *chname; /* Name of this channel */ -// int coord_frame; /* Which coordinate frame are we in? */ -// char *desc; /* Description for this type of a coil */ -// int coil_class; /* Coil class */ -// int type; /* Coil type */ -// int accuracy; /* Accuracy */ -// float size; /* Coil size */ -// float base; /* Baseline */ -// float r0[3]; /* Coil coordinate system origin */ -// float ex[3]; /* Coil coordinate system unit vectors */ -// float ey[3]; /* This stupid construction needs to be replaced with */ -// float ez[3]; /* a coordinate transformation */ -// int np; /* Number of integration points */ -// float **rmag; /* The field point locations */ -// float **cosmag; /* The corresponding direction cosines */ -// float *w; /* The weighting coefficients */ -// } *fwdCoil,fwdCoilRec; + Eigen::Matrix rmag; /**< The field point locations (np x 3). */ + Eigen::Matrix cosmag; /**< The corresponding direction cosines (np x 3). */ + Eigen::VectorXf w; /**< The weighting coefficients. */ }; //============================================================================================================= @@ -207,4 +179,4 @@ class FWDSHARED_EXPORT FwdCoil //============================================================================================================= } // NAMESPACE FWDLIB -#endif // FWDCOIL_H +#endif // FWD_COIL_H diff --git a/src/libraries/fwd/fwd_coil_set.cpp b/src/libraries/fwd/fwd_coil_set.cpp index 94adb6c1d8e..fd3e110baf2 100644 --- a/src/libraries/fwd/fwd_coil_set.cpp +++ b/src/libraries/fwd/fwd_coil_set.cpp @@ -40,6 +40,7 @@ #include "fwd_coil_set.h" #include "fwd_coil.h" +#include "fwd_bem_solution.h" #include @@ -244,8 +245,8 @@ static FwdCoil* fwd_add_coil_to_set(FwdCoilSet* set, return NULL; } - set->coils = REALLOC_6(set->coils,set->ncoil+1,FwdCoil*); - def = set->coils[set->ncoil++] = new FwdCoil(np); + set->coils.push_back(std::make_unique(np)); + def = set->coils.back().get(); def->type = type; def->coil_class = coil_class; @@ -264,11 +265,7 @@ static FwdCoil* fwd_add_coil_to_set(FwdCoilSet* set, FwdCoilSet::FwdCoilSet() { - coils = NULL; - ncoil = 0; coord_frame = FIFFV_COORD_UNKNOWN; - user_data = NULL; - user_data_free = NULL; } //============================================================================================================= @@ -281,11 +278,13 @@ FwdCoilSet::FwdCoilSet() FwdCoilSet::~FwdCoilSet() { - for (int k = 0; k < ncoil; k++) - delete coils[k]; - FREE_6(coils); +} + +//============================================================================================================= - this->fwd_free_coil_set_user_data(); +void FwdCoilSet::fwd_free_coil_set_user_data() +{ + user_data.reset(); } //============================================================================================================= @@ -303,10 +302,10 @@ FwdCoil *FwdCoilSet::create_meg_coil(const FiffChInfo& ch, int acc, const FiffCo /* * Simple linear search from the coil definitions */ - for (k = 0, def = NULL; k < this->ncoil; k++) { + for (k = 0, def = NULL; k < this->ncoil(); k++) { if ((this->coils[k]->type == (ch.chpos.coil_type & 0xFFFF)) && this->coils[k]->accuracy == acc) { - def = this->coils[k]; + def = this->coils[k].get(); } } if (!def) { @@ -347,8 +346,8 @@ FwdCoil *FwdCoilSet::create_meg_coil(const FiffChInfo& ch, int acc, const FiffCo for (p = 0; p < res->np; p++) { res->w[p] = def->w[p]; for (c = 0; c < 3; c++) { - res->rmag[p][c] = res->r0[c] + def->rmag[p][X_6]*res->ex[c] + def->rmag[p][Y_6]*res->ey[c] + def->rmag[p][Z_6]*res->ez[c]; - res->cosmag[p][c] = def->cosmag[p][X_6]*res->ex[c] + def->cosmag[p][Y_6]*res->ey[c] + def->cosmag[p][Z_6]*res->ez[c]; + res->rmag(p, c) = res->r0[c] + def->rmag(p, 0)*res->ex[c] + def->rmag(p, 1)*res->ey[c] + def->rmag(p, 2)*res->ez[c]; + res->cosmag(p, c) = def->cosmag(p, 0)*res->ex[c] + def->cosmag(p, 1)*res->ey[c] + def->cosmag(p, 2)*res->ez[c]; } } return res; @@ -372,8 +371,7 @@ FwdCoilSet *FwdCoilSet::create_meg_coils(const QList& chs, for (k = 0; k < nch; k++) { if ((next = this->create_meg_coil(chs.at(k),acc,t)) == Q_NULLPTR) goto bad; - res->coils = REALLOC_6(res->coils,res->ncoil+1,FwdCoil*); - res->coils[res->ncoil++] = next; + res->coils.push_back(std::unique_ptr(next)); } if (!t.isEmpty()) res->coord_frame = t.to; @@ -398,8 +396,7 @@ FwdCoilSet *FwdCoilSet::create_eeg_els(const QList& chs, for (k = 0; k < nch; k++) { if ((next = FwdCoil::create_eeg_el(chs.at(k),t)) == Q_NULLPTR) goto bad; - res->coils = REALLOC_6(res->coils,res->ncoil+1,FwdCoil*); - res->coils[res->ncoil++] = next; + res->coils.push_back(std::unique_ptr(next)); } if (!t.isEmpty()) res->coord_frame = t.to; @@ -461,37 +458,37 @@ FwdCoilSet *FwdCoilSet::read_coil_defs(const QString &name) /* * Read and verify data for each integration point */ - if (get_fval(in,def->w+p) != OK) + if (get_fval(in,def->w.data()+p) != OK) goto bad; - if (get_fval(in,def->rmag[p]+X_6) != OK) + if (get_fval(in,&def->rmag(p, 0)) != OK) goto bad; - if (get_fval(in,def->rmag[p]+Y_6) != OK) + if (get_fval(in,&def->rmag(p, 1)) != OK) goto bad; - if (get_fval(in,def->rmag[p]+Z_6) != OK) + if (get_fval(in,&def->rmag(p, 2)) != OK) goto bad; - if (get_fval(in,def->cosmag[p]+X_6) != OK) + if (get_fval(in,&def->cosmag(p, 0)) != OK) goto bad; - if (get_fval(in,def->cosmag[p]+Y_6) != OK) + if (get_fval(in,&def->cosmag(p, 1)) != OK) goto bad; - if (get_fval(in,def->cosmag[p]+Z_6) != OK) + if (get_fval(in,&def->cosmag(p, 2)) != OK) goto bad; - if (VEC_LEN_6(def->rmag[p]) > BIG) { - qWarning("Unreasonable integration point: %f %f %f mm (coil type = %d acc = %d)", 1000*def->rmag[p][X_6],1000*def->rmag[p][Y_6],1000*def->rmag[p][Z_6], def->type,def->accuracy); + if (VEC_LEN_6((&def->rmag(p, 0))) > BIG) { + qWarning("Unreasonable integration point: %f %f %f mm (coil type = %d acc = %d)", 1000*def->rmag(p, 0),1000*def->rmag(p, 1),1000*def->rmag(p, 2), def->type,def->accuracy); goto bad; } - size = VEC_LEN_6(def->cosmag[p]); + size = VEC_LEN_6((&def->cosmag(p, 0))); if (size <= 0) { - qWarning("Unreasonable normal: %f %f %f (coil type = %d acc = %d)", def->cosmag[p][X_6],def->cosmag[p][Y_6],def->cosmag[p][Z_6], def->type,def->accuracy); + qWarning("Unreasonable normal: %f %f %f (coil type = %d acc = %d)", def->cosmag(p, 0),def->cosmag(p, 1),def->cosmag(p, 2), def->type,def->accuracy); goto bad; } - normalize(def->cosmag[p]); + def->cosmag.row(p).normalize(); } } fclose(in); - printf("%d coil definitions read\n",res->ncoil); + printf("%d coil definitions read\n",res->ncoil()); return res; bad : { @@ -506,7 +503,6 @@ bad : { FwdCoilSet* FwdCoilSet::dup_coil_set(const FiffCoordTrans& t) const { FwdCoilSet* res; - FwdCoil* coil; if (!t.isEmpty()) { if (this->coord_frame != t.from) { @@ -520,11 +516,10 @@ FwdCoilSet* FwdCoilSet::dup_coil_set(const FiffCoordTrans& t) const else res->coord_frame = this->coord_frame; - res->coils = MALLOC_6(this->ncoil,FwdCoil*); - res->ncoil = this->ncoil; + res->coils.reserve(this->ncoil()); - for (int k = 0; k < this->ncoil; k++) { - coil = res->coils[k] = new FwdCoil(*(this->coils[k])); + for (int k = 0; k < this->ncoil(); k++) { + auto coil = std::make_unique(*(this->coils[k])); /* * Optional coordinate transformation */ @@ -535,11 +530,12 @@ FwdCoilSet* FwdCoilSet::dup_coil_set(const FiffCoordTrans& t) const FiffCoordTrans::apply_trans(coil->ez,t,FIFFV_NO_MOVE); for (int p = 0; p < coil->np; p++) { - FiffCoordTrans::apply_trans(coil->rmag[p],t,FIFFV_MOVE); - FiffCoordTrans::apply_trans(coil->cosmag[p],t,FIFFV_NO_MOVE); + FiffCoordTrans::apply_trans(&coil->rmag(p, 0),t,FIFFV_MOVE); + FiffCoordTrans::apply_trans(&coil->cosmag(p, 0),t,FIFFV_NO_MOVE); } coil->coord_frame = t.to; } + res->coils.push_back(std::move(coil)); } return res; } @@ -550,7 +546,7 @@ bool FwdCoilSet::is_planar_coil_type(int type) const { if (type == FIFFV_COIL_EEG) return false; - for (int k = 0; k < this->ncoil; k++) + for (int k = 0; k < this->ncoil(); k++) if (this->coils[k]->type == type) return this->coils[k]->coil_class == FWD_COILC_PLANAR_GRAD; return false; @@ -562,7 +558,7 @@ bool FwdCoilSet::is_axial_coil_type(int type) const { if (type == FIFFV_COIL_EEG) return false; - for (int k = 0; k < this->ncoil; k++) + for (int k = 0; k < this->ncoil(); k++) if (this->coils[k]->type == type) return (this->coils[k]->coil_class == FWD_COILC_MAG || this->coils[k]->coil_class == FWD_COILC_AXIAL_GRAD || @@ -576,7 +572,7 @@ bool FwdCoilSet::is_magnetometer_coil_type(int type) const { if (type == FIFFV_COIL_EEG) return false; - for (int k = 0; k < this->ncoil; k++) + for (int k = 0; k < this->ncoil(); k++) if (this->coils[k]->type == type) return this->coils[k]->coil_class == FWD_COILC_MAG; return false; diff --git a/src/libraries/fwd/fwd_coil_set.h b/src/libraries/fwd/fwd_coil_set.h index d5cc1a6b2c3..48f058e7478 100644 --- a/src/libraries/fwd/fwd_coil_set.h +++ b/src/libraries/fwd/fwd_coil_set.h @@ -34,8 +34,8 @@ * */ -#ifndef FWDCOILSET_H -#define FWDCOILSET_H +#ifndef FWD_COIL_SET_H +#define FWD_COIL_SET_H //============================================================================================================= // INCLUDES @@ -57,7 +57,8 @@ #include -typedef void (*fwdUserFreeFunc)(void *); /* General purpose */ +#include +#include //============================================================================================================= // DEFINE NAMESPACE FWDLIB @@ -65,7 +66,7 @@ typedef void (*fwdUserFreeFunc)(void *); /* General purpose */ namespace FWDLIB { - +class FwdBemSolution; //============================================================================================================= /** * Implements FwdCoilSet (Replaces *fwdCoilSet,fwdCoilSetRec; struct of MNE-C fwd_types.h). @@ -81,37 +82,21 @@ class FWDSHARED_EXPORT FwdCoilSet //========================================================================================================= /** * Constructs the Forward Coil Set description - * Refactored: fwd_new_coil_set */ FwdCoilSet(); -// //========================================================================================================= -// /** -// * Copy constructor. -// * -// * @param[in] p_FwdCoilSet FwdCoilSet which should be copied -// */ -// FwdCoilSet(const FwdCoilSet& p_FwdCoilSet); - //========================================================================================================= /** * Destroys the Forward Coil Set description - * Refactored: fwd_free_coil_set, fwd_free_coil_set_user_data */ ~FwdCoilSet(); - void fwd_free_coil_set_user_data() - { - if (user_data_free && user_data) - user_data_free(user_data); - user_data = NULL; - } + void fwd_free_coil_set_user_data(); //========================================================================================================= /** * Create a MEG coil definition using a database of templates * Change the coordinate frame if so desired - * Refactored: fwd_create_meg_coil (fwd_coil_def.c) * * @param[in] ch Channel information to use. * @param[in] acc Required accuracy. @@ -125,7 +110,6 @@ class FWDSHARED_EXPORT FwdCoilSet /** * Create a MEG coil set definition using a database of templates * Change the coordinate frame if so desired - * Refactored: fwd_create_meg_coils (fwd_coil_def.c) * * @param[in] ch Channel information to use. * @param[in] nch Number of channels. @@ -143,7 +127,6 @@ class FWDSHARED_EXPORT FwdCoilSet /** * Create a EEG coil set definition using a channel information * Change the coordinate frame if so desired - * Refactored: fwd_create_eeg_els (fwd_coil_def.c) * * @param[in] ch Channel information to use. * @param[in] nch Number of channels. @@ -158,7 +141,6 @@ class FWDSHARED_EXPORT FwdCoilSet //========================================================================================================= /** * Read a coil definitions from file - * Refactored: fwd_read_coil_defs (fwd_coil_def.c) * * @param[in] name File name to read from. * @@ -169,7 +151,6 @@ class FWDSHARED_EXPORT FwdCoilSet //========================================================================================================= /** * Make a coil set duplicate - * Refactored: fwd_dup_coil_set (fwd_coil_def.c) * * @param[in] t Transformation which should be applied to the duplicate. * @@ -180,7 +161,6 @@ class FWDSHARED_EXPORT FwdCoilSet //========================================================================================================= /** * Checks if a set of templates contains a planar coil of a specified type. - * Refactored: fwd_is_planar_coil_type (fwd_coil_def.c) * * @param[in] type This is the coil type we are interested in. * @@ -191,7 +171,6 @@ class FWDSHARED_EXPORT FwdCoilSet //========================================================================================================= /** * Checks if a set of templates contains an axial coil of a specified type. - * Refactored: fwd_is_axial_coil_type (fwd_coil_def.c) * * @param[in] type This is the coil type we are interested in. * @@ -202,7 +181,6 @@ class FWDSHARED_EXPORT FwdCoilSet //========================================================================================================= /** * Checks if a set of templates contains a magnetometer of a specified type. - * Refactored: fwd_is_magnetometer_coil_type (fwd_coil_def.c) * * @param[in] type This is the coil type we are interested in. * @@ -213,7 +191,6 @@ class FWDSHARED_EXPORT FwdCoilSet //========================================================================================================= /** * Checks if a set of templates contains an EEG electrode of a specified type. - * Refactored: fwd_is_eeg_electrode_type (fwd_coil_def.c) * * @param[in] type This is the coil type we are interested in. * @@ -222,20 +199,12 @@ class FWDSHARED_EXPORT FwdCoilSet bool is_eeg_electrode_type(int type) const; public: - FwdCoil **coils; /*< The coil or electrode positions >*/ - int ncoil; /*< Number of coils >*/ - int coord_frame; /*< Common coordinate frame >*/ - void *user_data; /*< We can put whatever in here >*/ - fwdUserFreeFunc user_data_free; - -// ### OLD STRUCT ### -// typedef struct { -// fwdCoil *coils; /* The coil or electrode positions */ -// int ncoil; -// int coord_frame; /* Common coordinate frame */ -// void *user_data; /* We can put whatever in here */ -// fwdUserFreeFunc user_data_free; -// } *fwdCoilSet,fwdCoilSetRec; /* A collection of the above */ + std::vector> coils; /**< The coil or electrode positions. */ + int coord_frame; /**< Common coordinate frame. */ + std::unique_ptr user_data; /**< Coil-specific BEM solution. */ + + /** Number of coils (convenience accessor). */ + inline int ncoil() const { return static_cast(coils.size()); } }; //============================================================================================================= @@ -243,4 +212,4 @@ class FWDSHARED_EXPORT FwdCoilSet //============================================================================================================= } // NAMESPACE FWDLIB -#endif // FWDCOILSET_H +#endif // FWD_COIL_SET_H diff --git a/src/libraries/fwd/fwd_comp_data.cpp b/src/libraries/fwd/fwd_comp_data.cpp index d0ef40892bd..a77f9a2c404 100644 --- a/src/libraries/fwd/fwd_comp_data.cpp +++ b/src/libraries/fwd/fwd_comp_data.cpp @@ -128,11 +128,10 @@ using namespace FWDLIB; FwdCompData::FwdCompData() :comp_coils (NULL) -,field (NULL) -,vec_field (NULL) -,field_grad (NULL) +,field (nullptr) +,vec_field (nullptr) +,field_grad (nullptr) ,client (NULL) -,client_free(NULL) ,set (NULL) ,work (NULL) ,vec_work (NULL) @@ -143,21 +142,17 @@ FwdCompData::FwdCompData() FwdCompData::~FwdCompData() { -// fwd_free_comp_data((void *)this); if(this->comp_coils) delete this->comp_coils; if(this->set) delete this->set; FREE_60(this->work); FREE_CMATRIX_60(this->vec_work); - - if (this->client_free && this->client) - this->client_free(this->client); } //============================================================================================================= -int FwdCompData::fwd_comp_field(float *rd, float *Q, FwdCoilSet *coils, float *res, void *client) +int FwdCompData::fwd_comp_field(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet *coils, float *res, void *client) /* * Calculate the compensated field (one dipole component) */ @@ -176,13 +171,13 @@ int FwdCompData::fwd_comp_field(float *rd, float *Q, FwdCoilSet *coils, float *r /* * Compensation needed? */ - if (!comp->comp_coils || comp->comp_coils->ncoil <= 0 || !comp->set || !comp->set->current) + if (!comp->comp_coils || comp->comp_coils->ncoil() <= 0 || !comp->set || !comp->set->current) return OK; /* * Workspace needed? */ if (!comp->work) - comp->work = MALLOC_60(comp->comp_coils->ncoil,float); + comp->work = MALLOC_60(comp->comp_coils->ncoil(),float); /* * Compute the field in the compensation coils */ @@ -192,33 +187,16 @@ int FwdCompData::fwd_comp_field(float *rd, float *Q, FwdCoilSet *coils, float *r * Compute the compensated field */ { - VectorXf resVec = Map(res, coils->ncoil); - VectorXf workVec = Map(comp->work, comp->comp_coils->ncoil); + VectorXf resVec = Map(res, coils->ncoil()); + VectorXf workVec = Map(comp->work, comp->comp_coils->ncoil()); int result = comp->set->apply(TRUE, resVec, workVec); - Map(res, coils->ncoil) = resVec; + Map(res, coils->ncoil()) = resVec; return result; } } //============================================================================================================= -void FwdCompData::fwd_free_comp_data(void *d) -{ - FwdCompData* comp = (FwdCompData*)d; - - if (!comp) - return; - - if (comp->client_free && comp->client) - comp->client_free(comp->client); - - if(comp) - delete(comp); - return; -} - -//============================================================================================================= - int FwdCompData::fwd_make_ctf_comp_coils(MNECTFCompDataSet *set, FwdCoilSet *coils, FwdCoilSet *comp_coils) /* The compensation coil set */ @@ -242,7 +220,7 @@ int FwdCompData::fwd_make_ctf_comp_coils(MNECTFCompDataSet *set, */ return OK; } - if (!coils || coils->ncoil <= 0) { + if (!coils || coils->ncoil() <= 0) { printf("Coil data missing in fwd_make_ctf_comp_coils"); return FAIL; } @@ -250,23 +228,23 @@ int FwdCompData::fwd_make_ctf_comp_coils(MNECTFCompDataSet *set, * Create the fake channel info which contain just enough information * for make_comp */ - for (k = 0; k < coils->ncoil; k++) { + for (k = 0; k < coils->ncoil(); k++) { chs.append(FiffChInfo()); - coil = coils->coils[k]; + coil = coils->coils[k].get(); chs[k].ch_name = coil->chname; chs[k].chpos.coil_type = coil->type; chs[k].kind = (coil->coil_class == FWD_COILC_EEG) ? FIFFV_EEG_CH : FIFFV_MEG_CH; } - nchan = coils->ncoil; - if (comp_coils && comp_coils->ncoil > 0) { - for (k = 0; k < comp_coils->ncoil; k++) { + nchan = coils->ncoil(); + if (comp_coils && comp_coils->ncoil() > 0) { + for (k = 0; k < comp_coils->ncoil(); k++) { compchs.append(FiffChInfo()); - coil = comp_coils->coils[k]; + coil = comp_coils->coils[k].get(); compchs[k].ch_name = coil->chname; compchs[k].chpos.coil_type = coil->type; compchs[k].kind = (coil->coil_class == FWD_COILC_EEG) ? FIFFV_EEG_CH : FIFFV_MEG_CH; } - ncomp = comp_coils->ncoil; + ncomp = comp_coils->ncoil(); } res = set->make_comp(chs,nchan,compchs,ncomp); @@ -281,8 +259,7 @@ FwdCompData *FwdCompData::fwd_make_comp_data(MNECTFCompDataSet *set, fwdFieldFunc field, fwdVecFieldFunc vec_field, fwdFieldGradFunc field_grad, - void *client, - fwdUserFreeFunc client_free) + void *client) /* * Compose a compensation data set */ @@ -305,12 +282,11 @@ FwdCompData *FwdCompData::fwd_make_comp_data(MNECTFCompDataSet *set, comp->vec_field = vec_field; comp->field_grad = field_grad; comp->client = client; - comp->client_free = client_free; if (fwd_make_ctf_comp_coils(comp->set, coils, comp->comp_coils) != OK) { - fwd_free_comp_data(comp); + delete comp; return NULL; } else { @@ -320,7 +296,7 @@ FwdCompData *FwdCompData::fwd_make_comp_data(MNECTFCompDataSet *set, //============================================================================================================= -int FwdCompData::fwd_comp_field_vec(float *rd, FwdCoilSet *coils, float **res, void *client) +int FwdCompData::fwd_comp_field_vec(const Eigen::Vector3f& rd, FwdCoilSet *coils, float **res, void *client) /* * Calculate the compensated field (all dipole components) */ @@ -340,13 +316,13 @@ int FwdCompData::fwd_comp_field_vec(float *rd, FwdCoilSet *coils, float **res, v /* * Compensation needed? */ - if (!comp->comp_coils || comp->comp_coils->ncoil <= 0 || !comp->set || !comp->set->current) + if (!comp->comp_coils || comp->comp_coils->ncoil() <= 0 || !comp->set || !comp->set->current) return OK; /* * Need workspace? */ if (!comp->vec_work) - comp->vec_work = ALLOC_CMATRIX_60(3,comp->comp_coils->ncoil); + comp->vec_work = ALLOC_CMATRIX_60(3,comp->comp_coils->ncoil()); /* * Compute the field at the compensation sensors */ @@ -356,18 +332,18 @@ int FwdCompData::fwd_comp_field_vec(float *rd, FwdCoilSet *coils, float **res, v * Compute the compensated field of three orthogonal dipoles */ for (k = 0; k < 3; k++) { - VectorXf resVec = Map(res[k], coils->ncoil); - VectorXf workVec = Map(comp->vec_work[k], comp->comp_coils->ncoil); + VectorXf resVec = Map(res[k], coils->ncoil()); + VectorXf workVec = Map(comp->vec_work[k], comp->comp_coils->ncoil()); if (comp->set->apply(TRUE, resVec, workVec) == FAIL) return FAIL; - Map(res[k], coils->ncoil) = resVec; + Map(res[k], coils->ncoil()) = resVec; } return OK; } //============================================================================================================= -int FwdCompData::fwd_comp_field_grad(float *rd, float *Q, FwdCoilSet* coils, float *res, float *xgrad, float *ygrad, float *zgrad, void *client) +int FwdCompData::fwd_comp_field_grad(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet* coils, float *res, float *xgrad, float *ygrad, float *zgrad, void *client) /* * Calculate the compensated field (one dipole component) */ @@ -386,15 +362,15 @@ int FwdCompData::fwd_comp_field_grad(float *rd, float *Q, FwdCoilSet* coils, flo /* * Compensation needed? */ - if (!comp->comp_coils || comp->comp_coils->ncoil <= 0 || !comp->set || !comp->set->current) + if (!comp->comp_coils || comp->comp_coils->ncoil() <= 0 || !comp->set || !comp->set->current) return OK; /* * Workspace needed? */ if (!comp->work) - comp->work = MALLOC_60(comp->comp_coils->ncoil,float); + comp->work = MALLOC_60(comp->comp_coils->ncoil(),float); if (!comp->vec_work) - comp->vec_work = ALLOC_CMATRIX_60(3,comp->comp_coils->ncoil); + comp->vec_work = ALLOC_CMATRIX_60(3,comp->comp_coils->ncoil()); /* * Compute the field in the compensation coils */ @@ -404,8 +380,8 @@ int FwdCompData::fwd_comp_field_grad(float *rd, float *Q, FwdCoilSet* coils, flo * Compute the compensated field */ { - int ncoil = coils->ncoil; - int ncomp_coil = comp->comp_coils->ncoil; + int ncoil = coils->ncoil(); + int ncomp_coil = comp->comp_coils->ncoil(); VectorXf resVec = Map(res, ncoil); VectorXf workVec = Map(comp->work, ncomp_coil); diff --git a/src/libraries/fwd/fwd_comp_data.h b/src/libraries/fwd/fwd_comp_data.h index 716f15755c7..c05be6ae67f 100644 --- a/src/libraries/fwd/fwd_comp_data.h +++ b/src/libraries/fwd/fwd_comp_data.h @@ -34,8 +34,8 @@ * */ -#ifndef FWDCOMPDATA_H -#define FWDCOMPDATA_H +#ifndef FWD_COMP_DATA_H +#define FWD_COMP_DATA_H //============================================================================================================= // INCLUDES @@ -93,27 +93,21 @@ class FWDSHARED_EXPORT FwdCompData //========================================================================================================= /** * Constructs the Forward Compensation Data - * Refactored: fwd_new_comp_data (fwd_comp.c) */ FwdCompData(); //========================================================================================================= /** * Destroys the Forward Compensation Data - * Refactored: fwd_free_comp_data (fwd_comp.c) */ ~FwdCompData(); - //============================= fwd_comp.c ============================= - - static int fwd_comp_field(float *rd,float *Q, FwdCoilSet* coils, float *res, void *client); + static int fwd_comp_field(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet* coils, float *res, void *client); /* * Routines to implement the reference channel compensation in field computations */ - static void fwd_free_comp_data(void *d); - static int fwd_make_ctf_comp_coils(MNELIB::MNECTFCompDataSet* set, /* The available compensation data */ FwdCoilSet* coils, /* The main coil set */ FwdCoilSet* comp_coils); @@ -124,12 +118,11 @@ class FWDSHARED_EXPORT FwdCompData fwdFieldFunc field, /* The field computation functions */ fwdVecFieldFunc vec_field, fwdFieldGradFunc field_grad, /* The field and gradient computation function */ - void *client, /* Client data to be passed to the above */ - fwdUserFreeFunc client_free); + void *client); /* Client data to be passed to the above */ - static int fwd_comp_field_vec(float *rd, FwdCoilSet* coils, float **res, void *client); + static int fwd_comp_field_vec(const Eigen::Vector3f& rd, FwdCoilSet* coils, float **res, void *client); - static int fwd_comp_field_grad(float *rd,float *Q, FwdCoilSet* coils, + static int fwd_comp_field_grad(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet* coils, float *res, float *xgrad, float *ygrad, float *zgrad, void *client); @@ -140,22 +133,8 @@ class FWDSHARED_EXPORT FwdCompData fwdVecFieldFunc vec_field; /* Computes the fields of all three dipole components */ fwdFieldGradFunc field_grad; /* Computes the field and gradient of one dipole direction */ void *client; /* Client data to pass to the above functions */ - fwdUserFreeFunc client_free; float *work; /* The work areas */ float **vec_work; - -// ### OLD STRUCT ### -//typedef struct { -// FWDLIB::MNECTFCompDataSet* set; /* The compensation data set */ -// FWDLIB::FwdCoilSet* comp_coils; /* The compensation coil definitions */ -// fwdFieldFunc field; /* Computes the field of given direction dipole */ -// fwdVecFieldFunc vec_field; /* Computes the fields of all three dipole components */ -// fwdFieldGradFunc field_grad; /* Computes the field and gradient of one dipole direction */ -// void *client; /* Client data to pass to the above functions */ -// fwdUserFreeFunc client_free; -// float *work; /* The work areas */ -// float **vec_work; -//} *fwdCompData,fwdCompDataRec; /* This structure is used in the compensated field calculations */ }; //============================================================================================================= @@ -163,4 +142,4 @@ class FWDSHARED_EXPORT FwdCompData //============================================================================================================= } // NAMESPACE FWDLIB -#endif // FWDCOMPDATA_H +#endif // FWD_COMP_DATA_H diff --git a/src/libraries/fwd/fwd_eeg_sphere_layer.h b/src/libraries/fwd/fwd_eeg_sphere_layer.h index 3d70c96c27d..57cb2f8ef91 100644 --- a/src/libraries/fwd/fwd_eeg_sphere_layer.h +++ b/src/libraries/fwd/fwd_eeg_sphere_layer.h @@ -34,8 +34,8 @@ * */ -#ifndef FWDEEGSPHERELAYER_H -#define FWDEEGSPHERELAYER_H +#ifndef FWD_EEG_SPHERE_LAYER_H +#define FWD_EEG_SPHERE_LAYER_H //============================================================================================================= // INCLUDES @@ -110,13 +110,6 @@ class FWDSHARED_EXPORT FwdEegSphereLayer float rad; /**< The actual rads. */ float rel_rad; /**< Relative rads. */ float sigma; /**< Conductivity. */ - -// ### OLD STRUCT ### -// typedef struct { -// float rad; /* The actual rads */ -// float rel_rad; /* Relative rads */ -// float sigma; /* Conductivity */ -// } *fwdEegSphereLayer,fwdEegSphereLayerRec; }; //============================================================================================================= @@ -124,4 +117,4 @@ class FWDSHARED_EXPORT FwdEegSphereLayer //============================================================================================================= } // NAMESPACE FWDLIB -#endif // FWDEEGSPHERELAYER_H +#endif // FWD_EEG_SPHERE_LAYER_H diff --git a/src/libraries/fwd/fwd_eeg_sphere_model.cpp b/src/libraries/fwd/fwd_eeg_sphere_model.cpp index fd09a2bdfa3..343401fef4f 100644 --- a/src/libraries/fwd/fwd_eeg_sphere_model.cpp +++ b/src/libraries/fwd/fwd_eeg_sphere_model.cpp @@ -48,6 +48,9 @@ #include #include + +#include +#include #include //============================================================================================================= @@ -508,7 +511,7 @@ void FwdEegSphereModel::calc_pot_components(double beta, double cgamma, double * //============================================================================================================= // fwd_multi_spherepot.c -int FwdEegSphereModel::fwd_eeg_multi_spherepot(float *rd, float *Q, float **el, int neeg, float *Vval, void *client) /* The model definition */ +int FwdEegSphereModel::fwd_eeg_multi_spherepot(float *rd, float *Q, const Eigen::Matrix& el, int neeg, float *Vval, void *client) /* The model definition */ /* * Compute the electric potentials in a set of electrodes in spherically * Symmetric head model. @@ -584,7 +587,7 @@ int FwdEegSphereModel::fwd_eeg_multi_spherepot(float *rd, float *Q, float **el, } for (k = 0; k < neeg; k++) { for (p = 0; p < 3; p++) - pos[p] = el[k][p] - m->r0[p]; + pos[p] = el(k, p) - m->r0[p]; /* * Should the position be scaled or not? */ @@ -635,7 +638,7 @@ int FwdEegSphereModel::fwd_eeg_multi_spherepot(float *rd, float *Q, float **el, //============================================================================================================= // fwd_multi_spherepot.c -int FwdEegSphereModel::fwd_eeg_multi_spherepot_coil1(float *rd, float *Q, FwdCoilSet *els, float *Vval, void *client) /* Client data will be the sphere model definition */ +int FwdEegSphereModel::fwd_eeg_multi_spherepot_coil1(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet *els, float *Vval, void *client) /* Client data will be the sphere model definition */ /* * Calculate the EEG in the sphere model using the fwdCoilSet structure * @@ -649,14 +652,14 @@ int FwdEegSphereModel::fwd_eeg_multi_spherepot_coil1(float *rd, float *Q, FwdCoi int k,c; FwdCoil* el; - for (k = 0; k < els->ncoil; k++, el++) { - el = els->coils[k]; + for (k = 0; k < els->ncoil(); k++, el++) { + el = els->coils[k].get(); if (el->coil_class == FWD_COILC_EEG) { if (el->np > nvval) { vval_one = REALLOC_1(vval_one,el->np,float); nvval = el->np; } - if (fwd_eeg_multi_spherepot(rd,Q,el->rmag,el->np,vval_one,client) != OK) { + if (fwd_eeg_multi_spherepot(const_cast(rd.data()),const_cast(Q.data()),el->rmag,el->np,vval_one,client) != OK) { FREE(vval_one); return FAIL; } @@ -672,7 +675,7 @@ int FwdEegSphereModel::fwd_eeg_multi_spherepot_coil1(float *rd, float *Q, FwdCoi //============================================================================================================= // fwd_multi_spherepot.c -bool FwdEegSphereModel::fwd_eeg_spherepot_vec( float *rd, float **el, int neeg, float **Vval_vec, void *client) +bool FwdEegSphereModel::fwd_eeg_spherepot_vec( float *rd, const Eigen::Matrix& el, int neeg, float **Vval_vec, void *client) { FwdEegSphereModel* m = (FwdEegSphereModel*)client; float fact = 0.25f/(float)M_PI; @@ -695,9 +698,9 @@ bool FwdEegSphereModel::fwd_eeg_spherepot_vec( float *rd, float **el, int ne * Initialize the arrays */ for (k = 0 ; k < neeg ; k++) { - Vval_vec[X_1][k] = 0.0; - Vval_vec[Y_1][k] = 0.0; - Vval_vec[Z_1][k] = 0.0; + Vval_vec[0][k] = 0.0; + Vval_vec[1][k] = 0.0; + Vval_vec[2][k] = 0.0; } /* * Ignore dipoles outside the innermost sphere @@ -728,10 +731,9 @@ bool FwdEegSphereModel::fwd_eeg_spherepot_vec( float *rd, float **el, int ne * Go over all electrodes */ for (k = 0; k < neeg ; k++) { - this_pos = el[k]; for (p = 0; p < 3; p++) - pos[p] = this_pos[p] - m->r0[p]; + pos[p] = el(k, p) - m->r0[p]; /* * Scale location onto the surface of the sphere */ @@ -766,25 +768,25 @@ bool FwdEegSphereModel::fwd_eeg_spherepot_vec( float *rd, float **el, int ne m1 = (c1 - c2*rrd); m2 = c2*rd2; - Vval_vec[X_1][k] = Vval_vec[X_1][k] + m->lambda[eq]*rd2_inv*(m1*rd[X_1] + m2*this_pos[X_1]); - Vval_vec[Y_1][k] = Vval_vec[Y_1][k] + m->lambda[eq]*rd2_inv*(m1*rd[Y_1] + m2*this_pos[Y_1]); - Vval_vec[Z_1][k] = Vval_vec[Z_1][k] + m->lambda[eq]*rd2_inv*(m1*rd[Z_1] + m2*this_pos[Z_1]); + Vval_vec[0][k] = Vval_vec[0][k] + m->lambda[eq]*rd2_inv*(m1*rd[0] + m2*this_pos[0]); + Vval_vec[1][k] = Vval_vec[1][k] + m->lambda[eq]*rd2_inv*(m1*rd[1] + m2*this_pos[1]); + Vval_vec[2][k] = Vval_vec[2][k] + m->lambda[eq]*rd2_inv*(m1*rd[2] + m2*this_pos[2]); } /* All electrodes done */ } /* All equivalent dipoles done */ /* * Finish by scaling by 1/(4*M_PI); */ for (k = 0; k < neeg; k++) { - Vval_vec[X_1][k] = fact*Vval_vec[X_1][k]; - Vval_vec[Y_1][k] = fact*Vval_vec[Y_1][k]; - Vval_vec[Z_1][k] = fact*Vval_vec[Z_1][k]; + Vval_vec[0][k] = fact*Vval_vec[0][k]; + Vval_vec[1][k] = fact*Vval_vec[1][k]; + Vval_vec[2][k] = fact*Vval_vec[2][k]; } return true; } //============================================================================================================= // fwd_multi_spherepot.c -int FwdEegSphereModel::fwd_eeg_spherepot_coil_vec(float *rd, FwdCoilSet* els, float **Vval_vec, void *client) +int FwdEegSphereModel::fwd_eeg_spherepot_coil_vec(const Eigen::Vector3f& rd, FwdCoilSet* els, float **Vval_vec, void *client) { float **vval_one = NULL; float val; @@ -792,15 +794,15 @@ int FwdEegSphereModel::fwd_eeg_spherepot_coil_vec(float *rd, FwdCoilSet* els, fl int k,c,p; FwdCoil* el; - for (k = 0; k < els->ncoil; k++, el++) { - el = els->coils[k]; + for (k = 0; k < els->ncoil(); k++, el++) { + el = els->coils[k].get(); if (el->coil_class == FWD_COILC_EEG) { if (el->np > nvval) { FREE_CMATRIX_1(vval_one); vval_one = ALLOC_CMATRIX_1(3,el->np); nvval = el->np; } - if (!fwd_eeg_spherepot_vec(rd,el->rmag,el->np,vval_one,client)) { + if (!fwd_eeg_spherepot_vec(const_cast(rd.data()),el->rmag,el->np,vval_one,client)) { FREE_CMATRIX_1(vval_one); return FAIL; } @@ -817,7 +819,7 @@ int FwdEegSphereModel::fwd_eeg_spherepot_coil_vec(float *rd, FwdCoilSet* els, fl //============================================================================================================= -int FwdEegSphereModel::fwd_eeg_spherepot_grad_coil(float *rd, float Q[], FwdCoilSet *coils, float Vval[], float xgrad[], float ygrad[], float zgrad[], void *client) /* Client data to be passed to some foward modelling routines */ +int FwdEegSphereModel::fwd_eeg_spherepot_grad_coil(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet *coils, float Vval[], float xgrad[], float ygrad[], float zgrad[], void *client) /* Client data to be passed to some foward modelling routines */ /* * Quick and dirty solution: use differences * @@ -839,13 +841,13 @@ int FwdEegSphereModel::fwd_eeg_spherepot_grad_coil(float *rd, float Q[], FwdCoil for (p = 0; p < 3; p++) { VEC_COPY_1(my_rd,rd); my_rd[p] = my_rd[p] + step; - if (fwd_eeg_spherepot_coil(my_rd,Q,coils,grads[p],client) == FAIL) + if (fwd_eeg_spherepot_coil(Eigen::Map(my_rd),Q,coils,grads[p],client) == FAIL) return FAIL; VEC_COPY_1(my_rd,rd); my_rd[p] = my_rd[p] - step; - if (fwd_eeg_spherepot_coil(my_rd,Q,coils,Vval,client) == FAIL) + if (fwd_eeg_spherepot_coil(Eigen::Map(my_rd),Q,coils,Vval,client) == FAIL) return FAIL; - for (q = 0; q < coils->ncoil; q++) + for (q = 0; q < coils->ncoil(); q++) grads[p][q] = (grads[p][q]-Vval[q])/step2; } if (Vval) { @@ -859,7 +861,7 @@ int FwdEegSphereModel::fwd_eeg_spherepot_grad_coil(float *rd, float Q[], FwdCoil // fwd_multi_spherepot.c int FwdEegSphereModel::fwd_eeg_spherepot( float *rd, /* Dipole position */ float *Q, /* Dipole moment */ - float **el, /* Electrode positions */ + const Eigen::Matrix& el, /* Electrode positions */ int neeg, /* Number of electrodes */ VectorXf& Vval, /* The potential values */ void *client) @@ -922,10 +924,9 @@ int FwdEegSphereModel::fwd_eeg_spherepot( float *rd, /* Dipole positio * Go over all electrodes */ for (k = 0; k < neeg ; k++) { - this_pos = el[k]; for (p = 0; p < 3; p++) - pos[p] = this_pos[p] - m->r0[p]; + pos[p] = el(k, p) - m->r0[p]; /* * Scale location onto the surface of the sphere */ @@ -974,7 +975,7 @@ int FwdEegSphereModel::fwd_eeg_spherepot( float *rd, /* Dipole positio //============================================================================================================= // fwd_multi_spherepot.c -int FwdEegSphereModel::fwd_eeg_spherepot_coil( float *rd, float *Q, FwdCoilSet* els, float *Vval, void *client) +int FwdEegSphereModel::fwd_eeg_spherepot_coil(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet* els, float *Vval, void *client) { VectorXf vval_one; float val; @@ -982,14 +983,14 @@ int FwdEegSphereModel::fwd_eeg_spherepot_coil( float *rd, float *Q, FwdCoilSet* int k,c; FwdCoil* el; - for (k = 0; k < els->ncoil; k++, el++) { - el = els->coils[k]; + for (k = 0; k < els->ncoil(); k++, el++) { + el = els->coils[k].get(); if (el->coil_class == FWD_COILC_EEG) { if (el->np > nvval) { vval_one.resize(el->np); nvval = el->np; } - if (fwd_eeg_spherepot(rd,Q,el->rmag,el->np,vval_one,client) != OK) { + if (fwd_eeg_spherepot(const_cast(rd.data()),const_cast(Q.data()),el->rmag,el->np,vval_one,client) != OK) { return FAIL; } for (c = 0, val = 0.0; c < el->np; c++) @@ -1063,7 +1064,6 @@ void fromDoubleEigenVector(const Eigen::VectorXd& from_vec, double *to_vec) fromDoubleEigenVector(from_vec, to_vec, from_vec.size()); } -//============================= fwd_fit_berg_scherg.c static double dot_dvectors (double *v1, double *v2, int nn) @@ -1075,7 +1075,6 @@ static double dot_dvectors (double *v1, result = result + v1[k]*v2[k]; return (result); } -//============================= fwd_fit_berg_scherg.c static int c_dsvd(double **mat, /* The matrix */ int m,int n, /* m rows n columns */ double *sing, /* Singular values (must have size @@ -1123,9 +1122,7 @@ static int c_dsvd(double **mat, /* The matrix */ * It is not too much of a problem */ -//============================= fwd_fit_berg_scherg.c -//============================= fwd_fit_berg_scherg.c namespace FWDLIB { @@ -1135,51 +1132,32 @@ namespace FWDLIB typedef struct { double lambda; /* Magnitude for the apparent dipole */ double mu; /* Distance multiplier for the apparent dipole */ -} *bergSchergPar,bergSchergParRec; +} bergSchergParRec; } // Namepsace -//============================= fwd_fit_berg_scherg.c -static int comp_pars(const void *p1,const void *p2) -/* - * Comparison function for sorting layers - */ -{ - bergSchergPar v1 = (bergSchergPar)p1; - bergSchergPar v2 = (bergSchergPar)p2; - - if (v1->mu > v2->mu) - return -1; - else if (v1->mu < v2->mu) - return 1; - else - return 0; -} - -//============================= fwd_fit_berg_scherg.c static void sort_parameters(VectorXd& mu,VectorXd& lambda,int nfit) /* * Sort the parameters so that largest mu comes first */ { - bergSchergPar pars = MALLOC_1(nfit,bergSchergParRec); + std::vector pars(nfit); for (int k = 0; k < nfit; k++) { pars[k].mu = mu[k]; pars[k].lambda = lambda[k]; } - - qsort (pars, nfit, sizeof(bergSchergParRec), comp_pars); + + std::sort(pars.begin(), pars.end(), [](const bergSchergParRec& a, const bergSchergParRec& b) { + return a.mu > b.mu; + }); for (int k = 0; k < nfit; k++) { mu[k] = pars[k].mu; lambda[k] = pars[k].lambda; } - - FREE(pars); } -//============================= fwd_fit_berg_scherg.c static bool report_fit(int loop, const VectorXd &fitpar, double Smin) @@ -1196,7 +1174,6 @@ static bool report_fit(int loop, return true; } -//============================= fwd_fit_berg_scherg.c static MatrixXd get_initial_simplex(const VectorXd &pars, double simplex_size) diff --git a/src/libraries/fwd/fwd_eeg_sphere_model.h b/src/libraries/fwd/fwd_eeg_sphere_model.h index 25f3d25c0fe..10449c7c6ad 100644 --- a/src/libraries/fwd/fwd_eeg_sphere_model.h +++ b/src/libraries/fwd/fwd_eeg_sphere_model.h @@ -34,8 +34,8 @@ * */ -#ifndef FWDEEGSPHEREMODEL_H -#define FWDEEGSPHEREMODEL_H +#ifndef FWD_EEG_SPHERE_MODEL_H +#define FWD_EEG_SPHERE_MODEL_H //============================================================================================================= // INCLUDES @@ -100,7 +100,6 @@ class FWDSHARED_EXPORT FwdEegSphereModel //========================================================================================================= /** * Constructs the Forward EEG Sphere Model - * Refactored: fwd_new_eeg_sphere_model * */ explicit FwdEegSphereModel(); @@ -108,7 +107,6 @@ class FWDSHARED_EXPORT FwdEegSphereModel //========================================================================================================= /** * Copy constructor. - * Refactored: fwd_dup_eeg_sphere_model * * @param[in] p_FwdEegSphereModel Forward EEG Sphere Model which should be copied. */ @@ -125,14 +123,12 @@ class FWDSHARED_EXPORT FwdEegSphereModel //========================================================================================================= /** * Destroys the Electric Current Dipole description - * Refactored: fwd_free_eeg_sphere_model */ virtual ~FwdEegSphereModel(); //========================================================================================================= /** * Set up the desired sphere model for EEG - * Refactored: setup_eeg_sphere_model (dipole_fit_setup.c) * * @param[in] eeg_model_file Contains the model specifications. * @param[in] eeg_model_name Name of the model to use. @@ -148,7 +144,6 @@ class FWDSHARED_EXPORT FwdEegSphereModel /** * fwd_multi_spherepot.c * Get the model depended weighting factor for n - * Refactored: fwd_eeg_get_multi_sphere_model_coeff (fwd_multi_spherepot.c) * * @param[in] n coefficient to which the expansion shopuld be calculated. * @@ -173,13 +168,13 @@ class FWDSHARED_EXPORT FwdEegSphereModel static int fwd_eeg_multi_spherepot(float *rd, /* Dipole position */ float *Q, /* Dipole moment */ - float **el, /* Electrode positions */ + const Eigen::Matrix& el, /* Electrode positions */ int neeg, /* Number of electrodes */ float *Vval, /* The potential values */ void *client); - static int fwd_eeg_multi_spherepot_coil1(float *rd, /* Dipole position */ - float *Q, /* Dipole moment */ + static int fwd_eeg_multi_spherepot_coil1(const Eigen::Vector3f& rd, /* Dipole position */ + const Eigen::Vector3f& Q, /* Dipole moment */ FwdCoilSet* els, /* Electrode positions */ float *Vval, /* The potential values */ void *client); @@ -199,7 +194,6 @@ class FWDSHARED_EXPORT FwdEegSphereModel * This routine uses the acceleration with help of equivalent sources * in the homogeneous sphere. * - * Refactored: fwd_eeg_spherepot_vec (fwd_multi_spherepot.c) * * * @param[in] rd Dipole position. @@ -209,7 +203,7 @@ class FWDSHARED_EXPORT FwdEegSphereModel * * @return true when successful. */ - static bool fwd_eeg_spherepot_vec (float *rd, float **el, int neeg, float **Vval_vec, void *client); + static bool fwd_eeg_spherepot_vec (float *rd, const Eigen::Matrix& el, int neeg, float **Vval_vec, void *client); //========================================================================================================= /** @@ -229,10 +223,10 @@ class FWDSHARED_EXPORT FwdEegSphereModel * * @return true when successful. */ - static int fwd_eeg_spherepot_coil_vec(float *rd, FwdCoilSet* els, float **Vval_vec, void *client); + static int fwd_eeg_spherepot_coil_vec(const Eigen::Vector3f& rd, FwdCoilSet* els, float **Vval_vec, void *client); - static int fwd_eeg_spherepot_grad_coil( float *rd, /* The dipole location */ - float Q[], /* The dipole components (xyz) */ + static int fwd_eeg_spherepot_grad_coil( const Eigen::Vector3f& rd, /* The dipole location */ + const Eigen::Vector3f& Q, /* The dipole components (xyz) */ FwdCoilSet* coils, /* The coil definitions */ float Vval[], /* Results */ float xgrad[], /* The derivatives with respect to */ @@ -258,7 +252,7 @@ class FWDSHARED_EXPORT FwdEegSphereModel * * @return true when successful. */ - static int fwd_eeg_spherepot( float *rd, float *Q, float **el, int neeg, Eigen::VectorXf& Vval, void *client); + static int fwd_eeg_spherepot( float *rd, float *Q, const Eigen::Matrix& el, int neeg, Eigen::VectorXf& Vval, void *client); //========================================================================================================= /** @@ -275,7 +269,7 @@ class FWDSHARED_EXPORT FwdEegSphereModel * * @return true when successful. */ - static int fwd_eeg_spherepot_coil(float *rd, float *Q, FwdCoilSet* els, float *Vval, void *client); + static int fwd_eeg_spherepot_coil(const Eigen::Vector3f& rd, const Eigen::Vector3f& Q, FwdCoilSet* els, float *Vval, void *client); //========================================================================================================= /** @@ -337,22 +331,6 @@ class FWDSHARED_EXPORT FwdEegSphereModel Eigen::VectorXf lambda; int nfit; /**< How many?. */ int scale_pos; /**< Scale the positions to the surface of the sphere?. */ - -// ### OLD STRUCT ### -// typedef struct { -// char *name; /* Textual identifier */ -// int nlayer; /* Number of layers */ -// fwdEegSphereLayer layers; /* An array of layers */ -// float r0[3]; /* The origin */ - -// double *fn; /* Coefficients saved to speed up the computations */ -// int nterms; /* How many? */ - -// float *mu; /* The Berg-Scherg equivalence parameters */ -// float *lambda; -// int nfit; /* How many? */ -// int scale_pos; /* Scale the positions to the surface of the sphere? */ -// } *fwdEegSphereModel,fwdEegSphereModelRec; }; //============================================================================================================= @@ -360,4 +338,4 @@ class FWDSHARED_EXPORT FwdEegSphereModel //============================================================================================================= } // NAMESPACE FWDLIB -#endif // FWDEEGSPHEREMODEL_H +#endif // FWD_EEG_SPHERE_MODEL_H diff --git a/src/libraries/fwd/fwd_eeg_sphere_model_set.h b/src/libraries/fwd/fwd_eeg_sphere_model_set.h index ed1a277dab5..651483ea916 100644 --- a/src/libraries/fwd/fwd_eeg_sphere_model_set.h +++ b/src/libraries/fwd/fwd_eeg_sphere_model_set.h @@ -30,12 +30,12 @@ * POSSIBILITY OF SUCH DAMAGE. * * - * @brief FwdEegSphereModelSet class declaration. + * @brief FwdEegSphereModelSet class declaration. * */ -#ifndef FWDEEGSPHEREMODELSET_H -#define FWDEEGSPHEREMODELSET_H +#ifndef FWD_EEG_SPHERE_MODEL_SET_H +#define FWD_EEG_SPHERE_MODEL_SET_H //============================================================================================================= // INCLUDES @@ -101,7 +101,6 @@ class FWDSHARED_EXPORT FwdEegSphereModelSet //========================================================================================================= /** * Destroys the Forward EEG Sphere Model Set description - * Refactored: fwd_free_eeg_sphere_model_set */ ~FwdEegSphereModelSet(); @@ -211,25 +210,13 @@ class FWDSHARED_EXPORT FwdEegSphereModelSet } public: -// QList m_qListModels; /**< Set of EEG sphere model definitions. */ - QList models; /**< Set of EEG sphere model definitions. */ - -// ### OLD STRUCT ### -// typedef struct { -// fwdEegSphereModel *models; /* Set of EEG sphere model definitions */ -// int nmodel; -// } *fwdEegSphereModelSet,fwdEegSphereModelSetRec; }; //============================================================================================================= // INLINE DEFINITIONS //============================================================================================================= -//inline qint32 FwdEegSphereModelSet::size() const -//{ -// return m_qListModels.size(); -//} } // NAMESPACE FWDLIB -#endif // FWDEEGSPHEREMODELSET_H +#endif // FWD_EEG_SPHERE_MODEL_SET_H diff --git a/src/libraries/fwd/fwd_field_map.cpp b/src/libraries/fwd/fwd_field_map.cpp index 4932e9e9e69..7904aab7c45 100644 --- a/src/libraries/fwd/fwd_field_map.cpp +++ b/src/libraries/fwd/fwd_field_map.cpp @@ -278,11 +278,11 @@ CoilData extractCoilData(const FwdCoil* coil, const Vector3d& r0) cd.w.resize(cd.np); for (int i = 0; i < cd.np; ++i) { - Vector3d rel = toVec3d(coil->rmag[i]) - r0; + Vector3d rel = coil->rmag.row(i).cast().transpose() - r0; double len = rel.norm(); cd.rmag[i] = (len > 0.0) ? Vector3d(rel / len) : Vector3d::Zero(); cd.rlen[i] = len; - cd.cosmag[i] = toVec3d(coil->cosmag[i]); + cd.cosmag[i] = coil->cosmag.row(i).cast().transpose(); cd.w[i] = static_cast(coil->w[i]); } return cd; @@ -301,10 +301,10 @@ CoilData extractCoilData(const FwdCoil* coil, const Vector3d& r0) */ MatrixXd doSelfDots(double intrad, const FwdCoilSet& coils, const Vector3d& r0, bool isMeg) { - const int nc = coils.ncoil; + const int nc = coils.ncoil(); std::vector cdata(nc); for (int i = 0; i < nc; ++i) { - cdata[i] = extractCoilData(coils.coils[i], r0); + cdata[i] = extractCoilData(coils.coils[i].get(), r0); } MatrixXd products = MatrixXd::Zero(nc, nc); @@ -348,12 +348,12 @@ MatrixXd doSurfaceDots(double intrad, const FwdCoilSet& coils, const MatrixX3f& rr, const MatrixX3f& nn, const Vector3d& r0, bool isMeg) { - const int nc = coils.ncoil; + const int nc = coils.ncoil(); const int nv = rr.rows(); std::vector cdata(nc); for (int i = 0; i < nc; ++i) { - cdata[i] = extractCoilData(coils.coils[i], r0); + cdata[i] = extractCoilData(coils.coils[i].get(), r0); } MatrixXd products = MatrixXd::Zero(nv, nc); @@ -392,8 +392,8 @@ MatrixXd doSurfaceDots(double intrad, const FwdCoilSet& coils, /** MEG noise stds: magnetometers get kMagStd, gradiometers get kGradStd. */ VectorXd adHocMegStds(const FwdCoilSet& coils) { - VectorXd stds(coils.ncoil); - for (int k = 0; k < coils.ncoil; ++k) { + VectorXd stds(coils.ncoil()); + for (int k = 0; k < coils.ncoil(); ++k) { stds(k) = coils.coils[k]->is_axial_coil() ? static_cast(kMagStd) : static_cast(kGradStd); } @@ -537,7 +537,7 @@ QSharedPointer FwdFieldMap::computeMegMapping( float intrad, float miss) { - if (coils.ncoil <= 0 || vertices.rows() == 0 || normals.rows() != vertices.rows()) { + if (coils.ncoil() <= 0 || vertices.rows() == 0 || normals.rows() != vertices.rows()) { return QSharedPointer(); } @@ -565,7 +565,7 @@ QSharedPointer FwdFieldMap::computeMegMapping( float intrad, float miss) { - if (coils.ncoil <= 0 || vertices.rows() == 0 || normals.rows() != vertices.rows()) { + if (coils.ncoil() <= 0 || vertices.rows() == 0 || normals.rows() != vertices.rows()) { return QSharedPointer(); } @@ -590,7 +590,7 @@ QSharedPointer FwdFieldMap::computeEegMapping( float intrad, float miss) { - if (coils.ncoil <= 0 || vertices.rows() == 0) { + if (coils.ncoil() <= 0 || vertices.rows() == 0) { return QSharedPointer(); } @@ -609,7 +609,7 @@ QSharedPointer FwdFieldMap::computeEegMapping( MatrixXd surfaceDots = doSurfaceDots(eegIntrad, coils, vertices, dummyNormals, r0, /*isMeg=*/false); // Ad-hoc noise for whitening - VectorXd stds = adHocEegStds(coils.ncoil); + VectorXd stds = adHocEegStds(coils.ncoil()); return computeMappingMatrix(selfDots, surfaceDots, stds, static_cast(miss)); } @@ -623,7 +623,7 @@ QSharedPointer FwdFieldMap::computeEegMapping( float intrad, float miss) { - if (coils.ncoil <= 0 || vertices.rows() == 0) { + if (coils.ncoil() <= 0 || vertices.rows() == 0) { return QSharedPointer(); } @@ -634,7 +634,7 @@ QSharedPointer FwdFieldMap::computeEegMapping( MatrixXd selfDots = doSelfDots(eegIntrad, coils, r0, /*isMeg=*/false); MatrixXd surfaceDots = doSurfaceDots(eegIntrad, coils, vertices, dummyNormals, r0, /*isMeg=*/false); - VectorXd stds = adHocEegStds(coils.ncoil); + VectorXd stds = adHocEegStds(coils.ncoil()); // Build SSP projector MatrixXd projOp; diff --git a/src/libraries/fwd/fwd_field_map.h b/src/libraries/fwd/fwd_field_map.h index 55d8c24af73..ba2f4cee755 100644 --- a/src/libraries/fwd/fwd_field_map.h +++ b/src/libraries/fwd/fwd_field_map.h @@ -28,7 +28,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * - * @brief Sphere-model field mapping. + * @brief FwdFieldMap class declaration. * * C++ port of MNE-Python field mapping (BSD-3-Clause, The MNE-Python contributors). * Source files: mne/forward/_lead_dots.py, mne/forward/_field_interpolation.py diff --git a/src/libraries/mne/mne_forwardsolution.cpp b/src/libraries/fwd/fwd_forward_solution.cpp similarity index 85% rename from src/libraries/mne/mne_forwardsolution.cpp rename to src/libraries/fwd/fwd_forward_solution.cpp index 6ff07eb8ead..89f74066a33 100644 --- a/src/libraries/mne/mne_forwardsolution.cpp +++ b/src/libraries/fwd/fwd_forward_solution.cpp @@ -1,6 +1,6 @@ //============================================================================================================= /** - * @file mne_forwardsolution.cpp + * @file fwd_forward_solution.cpp * @author Gabriel B Motta ; * Lorenz Esch ; * Matti Hamalainen ; @@ -31,7 +31,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * - * @brief MNEForwardSolution class implementation + * @brief FwdForwardSolution class definition. * */ @@ -39,15 +39,19 @@ // INCLUDES //============================================================================================================= -#include "mne_forwardsolution.h" +#include "fwd_forward_solution.h" #include +#include +#include + //============================================================================================================= // EIGEN INCLUDES //============================================================================================================= #include +#include #include #include @@ -55,13 +59,14 @@ // FIFF INCLUDES //============================================================================================================= -#include -#include -#include +#include +#include +#include #include #include #include +#include #include #include @@ -69,17 +74,29 @@ // USED NAMESPACES //============================================================================================================= -using namespace MNELIB; +using namespace FWDLIB; using namespace UTILSLIB; using namespace FSLIB; using namespace Eigen; using namespace FIFFLIB; +using namespace FWDLIB; + +//============================================================================================================= +// CONSTANTS +//============================================================================================================= + +constexpr int FAIL = -1; +constexpr int OK = 0; + +constexpr int X = 0; +constexpr int Y = 1; +constexpr int Z = 2; //============================================================================================================= // DEFINE MEMBER METHODS //============================================================================================================= -MNEForwardSolution::MNEForwardSolution() +FwdForwardSolution::FwdForwardSolution() : source_ori(-1) , surf_ori(false) , coord_frame(-1) @@ -96,7 +113,7 @@ MNEForwardSolution::MNEForwardSolution() //============================================================================================================= -MNEForwardSolution::MNEForwardSolution(QIODevice &p_IODevice, bool force_fixed, bool surf_ori, const QStringList& include, const QStringList& exclude, bool bExcludeBads) +FwdForwardSolution::FwdForwardSolution(QIODevice &p_IODevice, bool force_fixed, bool surf_ori, const QStringList& include, const QStringList& exclude, bool bExcludeBads) : source_ori(-1) , surf_ori(surf_ori) , coord_frame(-1) @@ -118,31 +135,56 @@ MNEForwardSolution::MNEForwardSolution(QIODevice &p_IODevice, bool force_fixed, //============================================================================================================= -MNEForwardSolution::MNEForwardSolution(const MNEForwardSolution &p_MNEForwardSolution) -: info(p_MNEForwardSolution.info) -, source_ori(p_MNEForwardSolution.source_ori) -, surf_ori(p_MNEForwardSolution.surf_ori) -, coord_frame(p_MNEForwardSolution.coord_frame) -, nsource(p_MNEForwardSolution.nsource) -, nchan(p_MNEForwardSolution.nchan) -, sol(p_MNEForwardSolution.sol) -, sol_grad(p_MNEForwardSolution.sol_grad) -, mri_head_t(p_MNEForwardSolution.mri_head_t) -, src(p_MNEForwardSolution.src) -, source_rr(p_MNEForwardSolution.source_rr) -, source_nn(p_MNEForwardSolution.source_nn) +FwdForwardSolution::FwdForwardSolution(const FwdForwardSolution &p_FwdForwardSolution) +: info(p_FwdForwardSolution.info) +, source_ori(p_FwdForwardSolution.source_ori) +, surf_ori(p_FwdForwardSolution.surf_ori) +, coord_frame(p_FwdForwardSolution.coord_frame) +, nsource(p_FwdForwardSolution.nsource) +, nchan(p_FwdForwardSolution.nchan) +, sol(p_FwdForwardSolution.sol) +, sol_grad(p_FwdForwardSolution.sol_grad) +, mri_head_t(p_FwdForwardSolution.mri_head_t) +, mri_filename(p_FwdForwardSolution.mri_filename) +, mri_id(p_FwdForwardSolution.mri_id) +, src(p_FwdForwardSolution.src) +, source_rr(p_FwdForwardSolution.source_rr) +, source_nn(p_FwdForwardSolution.source_nn) { } //============================================================================================================= -MNEForwardSolution::~MNEForwardSolution() +FwdForwardSolution& FwdForwardSolution::operator=(const FwdForwardSolution &other) { + if (this != &other) { + info = other.info; + source_ori = other.source_ori; + surf_ori = other.surf_ori; + coord_frame = other.coord_frame; + nsource = other.nsource; + nchan = other.nchan; + sol = other.sol; + sol_grad = other.sol_grad; + mri_head_t = other.mri_head_t; + mri_filename = other.mri_filename; + mri_id = other.mri_id; + src = other.src; + source_rr = other.source_rr; + source_nn = other.source_nn; + } + return *this; } //============================================================================================================= -void MNEForwardSolution::clear() +FwdForwardSolution::~FwdForwardSolution() +{ +} + +//============================================================================================================= + +void FwdForwardSolution::clear() { info.clear(); source_ori = -1; @@ -153,6 +195,8 @@ void MNEForwardSolution::clear() sol = FiffNamedMatrix::SDPtr(new FiffNamedMatrix()); sol_grad = FiffNamedMatrix::SDPtr(new FiffNamedMatrix()); mri_head_t.clear(); + mri_filename.clear(); + mri_id.clear(); src.clear(); source_rr = MatrixX3f(0,3); source_nn = MatrixX3f(0,3); @@ -160,7 +204,198 @@ void MNEForwardSolution::clear() //============================================================================================================= -MNEForwardSolution MNEForwardSolution::cluster_forward_solution(const AnnotationSet &p_AnnotationSet, +bool FwdForwardSolution::write(QIODevice& p_IODevice) const +{ + // + // Classify channels into MEG and EEG index sets + // + QList megIdx, eegIdx; + for (int k = 0; k < info.chs.size(); ++k) { + fiff_int_t kind = info.chs[k].kind; + if (kind == FIFFV_MEG_CH || kind == FIFFV_REF_MEG_CH) + megIdx.append(k); + else if (kind == FIFFV_EEG_CH) + eegIdx.append(k); + } + int nmeg = megIdx.size(); + int neeg = eegIdx.size(); + + // + // Compute the total number of active source vertices + // + int nvert = 0; + for (int k = 0; k < src.size(); ++k) + nvert += src[k].nuse; + + // + // Open the file, create the directory + // + FiffStream::SPtr t_pStream = FiffStream::start_file(p_IODevice); + t_pStream->start_block(FIFFB_MNE); + + // + // Information from the MRI file + // + { + t_pStream->start_block(FIFFB_MNE_PARENT_MRI_FILE); + + if (!mri_filename.isEmpty()) + t_pStream->write_string(FIFF_MNE_FILE_NAME, mri_filename); + if (!mri_id.isEmpty()) + t_pStream->write_id(FIFF_PARENT_FILE_ID, mri_id); + t_pStream->write_coord_trans(mri_head_t); + + t_pStream->end_block(FIFFB_MNE_PARENT_MRI_FILE); + } + + // + // Information from the measurement file + // + { + t_pStream->start_block(FIFFB_MNE_PARENT_MEAS_FILE); + + if (!info.filename.isEmpty()) + t_pStream->write_string(FIFF_MNE_FILE_NAME, info.filename); + if (!info.meas_id.isEmpty()) + t_pStream->write_id(FIFF_PARENT_BLOCK_ID, info.meas_id); + t_pStream->write_coord_trans(info.dev_head_t); + + int totalChan = nmeg + neeg; + t_pStream->write_int(FIFF_NCHAN, &totalChan); + + // Write channel infos with sequential scanNo + QList allChs; + for (int k = 0; k < nmeg; ++k) + allChs.append(info.chs[megIdx[k]]); + for (int k = 0; k < neeg; ++k) + allChs.append(info.chs[eegIdx[k]]); + for (int p = 0; p < allChs.size(); ++p) { + allChs[p].scanNo = p + 1; + t_pStream->write_ch_info(allChs[p]); + } + + t_pStream->write_bad_channels(info.bads); + + t_pStream->end_block(FIFFB_MNE_PARENT_MEAS_FILE); + } + + // + // Write the source spaces + // + for (int k = 0; k < src.size(); ++k) { + if (src[k].writeToStream(t_pStream, false) == FIFF_FAIL) { + t_pStream->close(); + return false; + } + } + + // + // Extract sub-matrices for MEG and EEG from the combined sol + // + auto extractRows = [](const FiffNamedMatrix& combined, + const QList& rowIdx) -> FiffNamedMatrix + { + int nRows = rowIdx.size(); + int nCols = combined.ncol; + MatrixXd data(nRows, nCols); + QStringList row_names; + for (int r = 0; r < nRows; ++r) { + data.row(r) = combined.data.row(rowIdx[r]); + row_names.append(combined.row_names[rowIdx[r]]); + } + FiffNamedMatrix sub; + sub.nrow = nRows; + sub.ncol = nCols; + sub.row_names = row_names; + sub.col_names = combined.col_names; + sub.data = data; + return sub; + }; + + int ori_val = (source_ori == FIFFV_MNE_FIXED_ORI) ? FIFFV_MNE_FIXED_ORI : FIFFV_MNE_FREE_ORI; + int frame = coord_frame; + + // + // MEG forward solution + // + if (nmeg > 0) { + t_pStream->start_block(FIFFB_MNE_FORWARD_SOLUTION); + + int val = FIFFV_MNE_MEG; + t_pStream->write_int(FIFF_MNE_INCLUDED_METHODS, &val); + t_pStream->write_int(FIFF_MNE_COORD_FRAME, &frame); + t_pStream->write_int(FIFF_MNE_SOURCE_ORIENTATION, &ori_val); + t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NPOINTS, &nvert); + t_pStream->write_int(FIFF_NCHAN, &nmeg); + + FiffNamedMatrix megSol = extractRows(*sol.data(), megIdx); + megSol.transpose_named_matrix(); + t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION, megSol); + + if (!sol_grad->isEmpty()) { + FiffNamedMatrix megGrad = extractRows(*sol_grad.data(), megIdx); + megGrad.transpose_named_matrix(); + t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION_GRAD, megGrad); + } + t_pStream->end_block(FIFFB_MNE_FORWARD_SOLUTION); + } + + // + // EEG forward solution + // + if (neeg > 0) { + t_pStream->start_block(FIFFB_MNE_FORWARD_SOLUTION); + + int val = FIFFV_MNE_EEG; + t_pStream->write_int(FIFF_MNE_INCLUDED_METHODS, &val); + t_pStream->write_int(FIFF_MNE_COORD_FRAME, &frame); + t_pStream->write_int(FIFF_MNE_SOURCE_ORIENTATION, &ori_val); + t_pStream->write_int(FIFF_NCHAN, &neeg); + t_pStream->write_int(FIFF_MNE_SOURCE_SPACE_NPOINTS, &nvert); + + FiffNamedMatrix eegSol = extractRows(*sol.data(), eegIdx); + eegSol.transpose_named_matrix(); + t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION, eegSol); + + if (!sol_grad->isEmpty()) { + FiffNamedMatrix eegGrad = extractRows(*sol_grad.data(), eegIdx); + eegGrad.transpose_named_matrix(); + t_pStream->write_named_matrix(FIFF_MNE_FORWARD_SOLUTION_GRAD, eegGrad); + } + t_pStream->end_block(FIFFB_MNE_FORWARD_SOLUTION); + } + + t_pStream->end_block(FIFFB_MNE); + t_pStream->end_file(); + t_pStream->close(); + t_pStream.clear(); + + // + // Update the directory + // + if (auto* qf = dynamic_cast(&p_IODevice)) { + QFile fileIn(qf->fileName()); + FiffStream::SPtr t_pStreamIn = FiffStream::open_update(fileIn); + if (t_pStreamIn) { + const auto& dir = t_pStreamIn->dir(); + for (int i = 0; i < dir.size(); ++i) { + if (dir[i]->kind == FIFF_DIR_POINTER) { + fiff_int_t dirpos = (fiff_int_t)t_pStreamIn->write_dir_entries(dir); + if (dirpos >= 0) + t_pStreamIn->write_dir_pointer(dirpos, dir[i]->pos); + break; + } + } + t_pStreamIn->close(); + } + } + + return true; +} + +//============================================================================================================= + +FwdForwardSolution FwdForwardSolution::cluster_forward_solution(const AnnotationSet &p_AnnotationSet, qint32 p_iClusterSize, MatrixXd& p_D, const FiffCov &p_pNoise_cov, @@ -169,15 +404,15 @@ MNEForwardSolution MNEForwardSolution::cluster_forward_solution(const Annotation { printf("Cluster forward solution using %s.\n", p_sMethod.toUtf8().constData()); - MNEForwardSolution p_fwdOut = MNEForwardSolution(*this); + FwdForwardSolution p_fwdOut = FwdForwardSolution(*this); //Check if cov naming conventions are matching if(!IOUtils::check_matching_chnames_conventions(p_pNoise_cov.names, p_pInfo.ch_names) && !p_pNoise_cov.names.isEmpty() && !p_pInfo.ch_names.isEmpty()) { if(IOUtils::check_matching_chnames_conventions(p_pNoise_cov.names, p_pInfo.ch_names, true)) { - qWarning("MNEForwardSolution::cluster_forward_solution - Cov names do match with info channel names but have a different naming convention."); + qWarning("FwdForwardSolution::cluster_forward_solution - Cov names do match with info channel names but have a different naming convention."); //return p_fwdOut; } else { - qWarning("MNEForwardSolution::cluster_forward_solution - Cov channel names do not match with info channel names."); + qWarning("FwdForwardSolution::cluster_forward_solution - Cov channel names do not match with info channel names."); //return p_fwdOut; } } @@ -699,9 +934,9 @@ MNEForwardSolution MNEForwardSolution::cluster_forward_solution(const Annotation //============================================================================================================= -MNEForwardSolution MNEForwardSolution::reduce_forward_solution(qint32 p_iNumDipoles, MatrixXd& p_D) const +FwdForwardSolution FwdForwardSolution::reduce_forward_solution(qint32 p_iNumDipoles, MatrixXd& p_D) const { - MNEForwardSolution p_fwdOut = MNEForwardSolution(*this); + FwdForwardSolution p_fwdOut = FwdForwardSolution(*this); bool isFixed = p_fwdOut.isFixedOrient(); qint32 np = isFixed ? p_fwdOut.sol->data.cols() : p_fwdOut.sol->data.cols()/3; @@ -804,14 +1039,14 @@ MNEForwardSolution MNEForwardSolution::reduce_forward_solution(qint32 p_iNumDipo //============================================================================================================= -FiffCov MNEForwardSolution::compute_depth_prior(const MatrixXd &Gain, const FiffInfo &gain_info, bool is_fixed_ori, double exp, double limit, const MatrixXd &patch_areas, bool limit_depth_chs) +FiffCov FwdForwardSolution::compute_depth_prior(const MatrixXd &Gain, const FiffInfo &gain_info, bool is_fixed_ori, double exp, double limit, const MatrixXd &patch_areas, bool limit_depth_chs) { printf("\tCreating the depth weighting matrix...\n"); MatrixXd G(Gain); // If possible, pick best depth-weighting channels if(limit_depth_chs) - MNEForwardSolution::restrict_gain_matrix(G, gain_info); + FwdForwardSolution::restrict_gain_matrix(G, gain_info); VectorXd d; // Compute the gain matrix @@ -913,7 +1148,7 @@ FiffCov MNEForwardSolution::compute_depth_prior(const MatrixXd &Gain, const Fiff //============================================================================================================= -FiffCov MNEForwardSolution::compute_orient_prior(float loose) +FiffCov FwdForwardSolution::compute_orient_prior(float loose) { bool is_fixed_ori = this->isFixedOrient(); qint32 n_sources = this->sol->data.cols(); @@ -962,10 +1197,10 @@ FiffCov MNEForwardSolution::compute_orient_prior(float loose) //============================================================================================================= -MNEForwardSolution MNEForwardSolution::pick_channels(const QStringList& include, +FwdForwardSolution FwdForwardSolution::pick_channels(const QStringList& include, const QStringList& exclude) const { - MNEForwardSolution fwd(*this); + FwdForwardSolution fwd(*this); if(include.size() == 0 && exclude.size() == 0) return fwd; @@ -1026,7 +1261,7 @@ MNEForwardSolution MNEForwardSolution::pick_channels(const QStringList& include, //============================================================================================================= -MNEForwardSolution MNEForwardSolution::pick_regions(const QList