gemprf 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. gem/__init__.py +10 -0
  2. gem/analysis/__init__.py +6 -0
  3. gem/analysis/prf_analysis.py +72 -0
  4. gem/analysis/prf_r2_variance_explain.py +101 -0
  5. gem/configs/__init__.py +6 -0
  6. gem/configs/analysis_configs/analysis_additional_params.h5 +0 -0
  7. gem/configs/analysis_configs/analysis_config.xml +124 -0
  8. gem/configs/config_manager.py +174 -0
  9. gem/configs/default_config/default_config - old before concatenation.xml +141 -0
  10. gem/configs/default_config/default_config.xml +171 -0
  11. gem/configs/default_config/new_concatenationDummyTest_config.xml +180 -0
  12. gem/configs/default_config/new_concatenation_config.xml +180 -0
  13. gem/configs/gem_xml_utils.py +30 -0
  14. gem/configs/multiproc_prfspace_config/asus_analysis_config.xml +165 -0
  15. gem/configs/multiproc_prfspace_config/dgx_analysis_config.xml +165 -0
  16. gem/data/__init__.py +6 -0
  17. gem/data/bids_handler.py +208 -0
  18. gem/data/gem_bids_concatenation_data_info.py +42 -0
  19. gem/data/gem_bids_individual_run_data_info.py +35 -0
  20. gem/data/gem_stimulus_file_info.py +16 -0
  21. gem/data/observed_data.py +42 -0
  22. gem/fitting/__init__.py +6 -0
  23. gem/fitting/hpc_coefficient_matrix.py +188 -0
  24. gem/fitting/hpc_grid_fit.py +184 -0
  25. gem/fitting/hpc_refine_fit.py +632 -0
  26. gem/init_setup.py +136 -0
  27. gem/kernels/gaussian_kernel.cu +208 -0
  28. gem/kernels/gaussian_kernel.ptx +1408 -0
  29. gem/model/__init__.py +6 -0
  30. gem/model/prf_DoG_model.py +32 -0
  31. gem/model/prf_gaussian_model.py +121 -0
  32. gem/model/prf_model.py +130 -0
  33. gem/model/prf_stimulus.py +146 -0
  34. gem/model/selected_prf_model.py +17 -0
  35. gem/run/__init__.py +0 -0
  36. gem/run/run_gem_prf_analysis.py +728 -0
  37. gem/run_gem.py +42 -0
  38. gem/signals/__init__.py +6 -0
  39. gem/signals/hrf_generator.py +64 -0
  40. gem/signals/orthogonalization_matrix.py +40 -0
  41. gem/signals/signal_synthesizer.py +281 -0
  42. gem/space/PRFNeighbours.py +4 -0
  43. gem/space/PRFSpace.py +569 -0
  44. gem/space/__init__.py +6 -0
  45. gem/space/coefficient_matrix.py +43 -0
  46. gem/tools/__init__.py +6 -0
  47. gem/tools/json_file_operations.py +43 -0
  48. gem/utils/__init__.py +6 -0
  49. gem/utils/gem_gpu_manager.py +107 -0
  50. gem/utils/gem_h5_file_handler.py +24 -0
  51. gem/utils/gem_load_estimations.py +89 -0
  52. gem/utils/gem_write_to_file.py +121 -0
  53. gem/utils/hpc_cupy_utils.py +149 -0
  54. gem/utils/logger.py +52 -0
  55. gemprf-0.1.1.dist-info/METADATA +98 -0
  56. gemprf-0.1.1.dist-info/RECORD +59 -0
  57. gemprf-0.1.1.dist-info/WHEEL +5 -0
  58. gemprf-0.1.1.dist-info/licenses/LICENSE +28 -0
  59. gemprf-0.1.1.dist-info/top_level.txt +1 -0
gem/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ from . import model
2
+ from .model import *
3
+
4
+ from . import tools
5
+ from .tools import *
6
+
7
+ from . import space
8
+ from .space import *
9
+
10
+ from .run_gem import run
@@ -0,0 +1,6 @@
1
+ class my_class(object):
2
+ pass
3
+
4
+
5
+
6
+
@@ -0,0 +1,72 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+
4
+ "@Author : Siddharth Mittal",
5
+ "@Version : 1.0",
6
+ "@Contact : siddharth.mittal@meduniwien.ac.at",
7
+ "@License : (C)Copyright 2024, Medical University of Vienna",
8
+ "@Desc : None",
9
+
10
+ """
11
+
12
+ import numpy as np
13
+ import cupy as cp
14
+ from typing import List
15
+
16
+ from gem.model.prf_model import PRFModel
17
+ from gem.model.selected_prf_model import SelectedPRFModel
18
+ from gem.model.prf_stimulus import Stimulus
19
+ from gem.space.PRFSpace import PRFSpace
20
+ from gem.utils.hpc_cupy_utils import HpcUtils as gpu_utils
21
+
22
+
23
+ class PRFAnalysis():
24
+ def __init__(self, prf_space : PRFSpace, stimulus : Stimulus) -> None:
25
+ self.__prf_points = prf_space
26
+ self.__stimulus = stimulus
27
+ self.__model_signals_batches = None
28
+ self.__dervatives_signals_batches_list = None
29
+ self.__error_e = None
30
+ self.__error_de_dtheta_list = None # contains a list of all derivative errors w.r.t each parameter
31
+
32
+ @property
33
+ def prf_points(self)-> PRFSpace:
34
+ return self.__prf_points
35
+
36
+ @property
37
+ def stimulus(self)-> Stimulus:
38
+ return self.__stimulus
39
+
40
+ @property
41
+ def orthonormalized_S_batches(self)-> List[cp.ndarray]:
42
+ return self.__model_signals_batches
43
+
44
+ @orthonormalized_S_batches.setter
45
+ def orthonormalized_S_batches(self, new_value)-> None:
46
+ self.__model_signals_batches = new_value
47
+
48
+ @property
49
+ def orthonormalized_dS_dtheta_batches_list(self)-> List[List[cp.ndarray]]:
50
+ return self.__dervatives_signals_batches_list
51
+
52
+ @orthonormalized_dS_dtheta_batches_list.setter
53
+ def orthonormalized_dS_dtheta_batches_list(self, new_value)-> None:
54
+ self.__dervatives_signals_batches_list = new_value
55
+
56
+ @property
57
+ def error_e(self)-> cp.ndarray:
58
+ return self.__error_e
59
+
60
+ @error_e.setter
61
+ def error_e(self, new_value)-> None:
62
+ self.__error_e = new_value
63
+
64
+ @property
65
+ def error_de_dtheta_list(self)-> List[cp.ndarray]:
66
+ return self.__error_de_dtheta_list
67
+
68
+ @error_de_dtheta_list.setter
69
+ def error_de_dtheta_list(self, new_value)-> None:
70
+ self.__error_de_dtheta_list = new_value
71
+
72
+
@@ -0,0 +1,101 @@
1
+ import numpy as np
2
+ import cupy as cp
3
+ from gem.tools.json_file_operations import JsonMgr
4
+ from gem.utils.hpc_cupy_utils import HpcUtils as Utils
5
+ from gem.model import prf_model
6
+ from gem.signals.signal_synthesizer import SignalSynthesizer
7
+ from gem.model.prf_model import GaussianModelParams
8
+ from gem.utils.gem_gpu_manager import GemGpuManager as ggm
9
+
10
+ class R2:
11
+ @classmethod
12
+ def get_r2_new_method_with_epsilon_as_Fx(cls, Y_signals_gpu, O_gpu, epsilon_results_cpu):
13
+ r2 = []
14
+ R_Rt_gpu = cp.eye(O_gpu.shape[0]) - O_gpu
15
+
16
+ num_y_signals = len(epsilon_results_cpu)
17
+ for yIdx in range (num_y_signals):
18
+ y = Y_signals_gpu[:, yIdx]
19
+ yty = y.T @ y
20
+ ystar = O_gpu @ y
21
+ ystarT_ystar_inv = (ystar.T @ ystar) ** (-1)
22
+ e = (float(epsilon_results_cpu[yIdx])) ** 2 #<----------------------------------
23
+ yt_RRt_y = (y.T @ R_Rt_gpu) @ y
24
+ r2_gpu = 1 - ((yty - e - yt_RRt_y) * ystarT_ystar_inv)
25
+ r2.append(float(r2_gpu))
26
+
27
+ return r2
28
+
29
+ @classmethod
30
+ def get_r2_num_den_method_with_epsilon_as_yTs(cls, Y_signals_gpu, O_gpu, refined_matching_results, refined_S_cpu):
31
+ with cp.cuda.Device(ggm.get_instance().default_gpu_id):
32
+ numerators_gpu , denominators_gpu = cls.get_r2_numerator_denominator_terms(Y_signals_gpu, O_gpu, refined_matching_results, refined_S_cpu)
33
+ r2_results_gpu = 1 - (numerators_gpu / denominators_gpu)
34
+
35
+ return cp.asnumpy(r2_results_gpu)
36
+
37
+ @classmethod # R2 numerator = (yty - e - yt_RRt_y)
38
+ def get_r2_numerator_denominator_terms(cls, Y_signals_gpu, O_gpu, refined_matching_results, refined_signals_cpu: np.ndarray):
39
+ with cp.cuda.Device(ggm.get_instance().default_gpu_id):
40
+ refined_signals_gpu = cp.asarray(refined_signals_cpu)
41
+ num_y_signals = Y_signals_gpu.shape[1]
42
+
43
+ R_Rt_gpu = cp.eye(O_gpu.shape[0]) - O_gpu
44
+
45
+ # Compute yty for each signal
46
+ yty = cp.sum(Y_signals_gpu ** 2, axis=0)
47
+
48
+ # Compute ystar and ystarT_ystar
49
+ ystar = O_gpu @ Y_signals_gpu
50
+ ystarT_ystar = cp.sum(ystar ** 2, axis=0)
51
+
52
+ # Check for NaN refined results
53
+ nan_mask = np.isnan(refined_matching_results).all(axis=1)
54
+ nan_mask = cp.asarray(nan_mask)
55
+
56
+ # Check for zero signals
57
+ s = refined_signals_gpu
58
+ zero_mask = np.all(refined_signals_cpu == 0, axis=1)
59
+ zero_mask = cp.asarray(zero_mask)
60
+
61
+ # Compute s_star
62
+ s_star = O_gpu @ s.T # shape: [timepoints, num_signals]
63
+
64
+ # Normalize s_star to get s_prime
65
+ s_prime = s_star * (cp.sum(s_star ** 2, axis=0) ** (-0.5))
66
+
67
+ # Compute error-term e = (y.T @ s_prime)^2
68
+ e = cp.sum(Y_signals_gpu * s_prime, axis=0) ** 2 #<----------------------------------
69
+
70
+ # Compute yt_RRt_y
71
+ # # yt_RRt_y = cp.sum(Y_signals_gpu * (R_Rt_gpu @ Y_signals_gpu), axis=0)
72
+ yt_R = Y_signals_gpu * (R_Rt_gpu @ Y_signals_gpu)
73
+ yt_RRt_y = cp.sum(yt_R, axis=0)
74
+
75
+ # Compute final numerator and denominator
76
+ num = yty - e - yt_RRt_y
77
+ den = ystarT_ystar
78
+
79
+ # Apply masks for nan/zero
80
+ num = cp.where(nan_mask, 2.0, num)
81
+ num = cp.where(zero_mask & ~nan_mask, 3.0, num)
82
+ den = cp.where(nan_mask | zero_mask, 1.0, den)
83
+
84
+ return num, den
85
+
86
+ @classmethod
87
+ def format_in_json_format(cls, r2_results, refined_matching_results, refined_signal_timecourses, refined_signals_present = True):
88
+ json_data_results_with_r2 = []
89
+
90
+ # default r2 value
91
+ for i in range((len(refined_matching_results))):
92
+ r2 = float(r2_results[i])
93
+ muX, muY, sigma = refined_matching_results[i]
94
+ if refined_signals_present:
95
+ refined_signal_timecourse = refined_signal_timecourses[i]
96
+ else:
97
+ refined_signal_timecourse = np.array([None])
98
+ json_entry = JsonMgr.args2jsonEntry(muX, muY, sigma, r2, refined_signal_timecourse)
99
+ json_data_results_with_r2.append(json_entry)
100
+
101
+ return json_data_results_with_r2
@@ -0,0 +1,6 @@
1
+ class my_class(object):
2
+ pass
3
+
4
+
5
+
6
+
@@ -0,0 +1,124 @@
1
+ <!-- XML representation of JSON data -->
2
+ <!--
3
+ "@Author : Siddharth Mittal",
4
+ "@Version : 1.0",
5
+ "@Contact : siddharth.mittal@meduniwien.ac.at",
6
+ "@License : (C)Copyright 2024, Medical University of Vienna"
7
+ -->
8
+
9
+ <!-- NOTE for Analysis: For DUMMY/Analsis, set correct Visual field at two positions, one for the <stimulus> and another for <search_space>-->
10
+
11
+
12
+ <root>
13
+ <!-- Path information -->
14
+ <path_to_append>.../</path_to_append>
15
+ <refine_fitting enable="True" refinefit_on_gpu="True"/> <!-- Execute refine fitting on GPU if sufficient GPU memory is available -->
16
+
17
+ <!-- Stimulus data -->
18
+ <stimulus>
19
+ <!-- File path for stimulus -->
20
+ <directory>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/stimsim24/BIDS/derivatives/prfprepare/analysis-01/sub-001/stimuli</directory>
21
+ <visual_field>9</visual_field> <!-- Visual field Radius, NOTE: Use "10" for simulated data, else "9" -->
22
+ <width>101</width>
23
+ <height>101</height>
24
+ <binarization enable="False" threshold="0"/> <!-- stimulus will be binarized if enabled, all values above threshold will be set to 1 and below to 0 -->
25
+
26
+ <!-- Compute model signals with high-res stimulus then downsample: "num_frames_downsampled" = your fMRI timecourse length, "slice_time_ref" for details see https://www.alivelearn.net/?p=1037 -->
27
+ <high_temporal_resolution enable="false" num_frames_downsampled="240" slice_time_ref="0.5"/>
28
+ </stimulus>
29
+
30
+ <!-- Input Data -->
31
+ <input_datasrc>
32
+ <BIDS enable="True" run_type="individual"> <!-- Set to "True" if the input data is organized in BIDS format, select "individual" or "concatenated" run type -->
33
+ <basepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/stimsim24/BIDS</basepath>
34
+ <append_to_basepath>derivatives, prfprepare</append_to_basepath> <!-- specify if the "analysis-xx" -->
35
+ <!-- <basepath>D:/results/fmri/generic/</basepath> -->
36
+
37
+ <!-- Directory name for results; backups created with timestamp if overwrite="True" -->
38
+ <results_anaylsis_id overwrite="False">GEMpRF</results_anaylsis_id>
39
+
40
+ <!--
41
+ NOTE: Case sensitive, provide the exact values!!!
42
+ For the analysis, sub, ses, task, run, hemi,
43
+ either provide comma separated values or specify "all" (to go through every available analysis)
44
+ Sample: <analysis>01, 02</analysis> or <analysis>all</analysis>
45
+ -->
46
+ <analysis>01</analysis>
47
+ <sub>001, 002</sub>
48
+ <hemi>all</hemi>
49
+
50
+ <!--INDIVIDUAL Task/stimulus Analysis-->
51
+ <individual>
52
+ <task>bar</task> <!--name of the stimulus, this will be used to find the file in the stimulus directory, ONLY one value is allowed-->
53
+ <ses>001nn</ses> <!-- comma separated values or specify "all" -->
54
+ <run>01, 02</run> <!-- comma separated values or specify "all" -->
55
+ </individual>
56
+
57
+ <!--CONCATENATED Analysis-->
58
+ <concatenated>
59
+ <concatenate_item>
60
+ <ses>001</ses> <!-- ONLY one value is allowed-->
61
+ <task>bar</task> <!-- ONLY one value is allowed-->
62
+ <run>02</run> <!-- ONLY one value is allowed-->
63
+ </concatenate_item>
64
+ <concatenate_item>
65
+ <ses>001</ses>
66
+ <task>bar</task>
67
+ <run>04</run>
68
+ </concatenate_item>
69
+ </concatenated>
70
+ </BIDS>
71
+
72
+ <!--OR, make Bids False and specify directly single/multiple filepaths -->
73
+ <fixed_paths>
74
+ <measured_data_filepath> <!-- Measured input data filepaths -->
75
+ <filepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/tests/gem_generic_test/BIDS/sub-001/ses-3n2/func/sub-001_ses-3n2_task-prf_acq-normal_run-01_bold.nii.gz</filepath>
76
+ <filepath>dummy////sub-001/ses-3n2/func/sub-001_ses-3n2_task-prf_acq-normal_run-01_bold.nii.gz</filepath>
77
+ </measured_data_filepath>
78
+ <results> <!-- Results filepaths realted -->
79
+ <basepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/tests/gem_generic_test/derivatives/prfanalyze-gem/analysis-01/sub-001/ses-3n2/</basepath> <!-- Base path for results -->
80
+ <custom_filename_postfix>_[gem-new_data-simulated_v1_151x151x8_refine-validation]</custom_filename_postfix> <!-- Custom filename postfix -->
81
+ <prepend_date>True</prepend_date> <!-- Flag to prepend date -->
82
+ </results>
83
+ </fixed_paths>
84
+ </input_datasrc>
85
+
86
+ <!-- Analysis Model -->
87
+ <pRF_model>
88
+ <model>2d_gaussian</model> <!-- 2d_gauss, (DoG, CSS not avaiable at the moment) -->
89
+ </pRF_model>
90
+
91
+ <!-- Measured data section -->
92
+ <measured_data>
93
+ <width>101</width>
94
+ <height>101</height>
95
+ <batches>200</batches>
96
+ </measured_data>
97
+
98
+ <!-- GPU configuration -->
99
+ <gpu>
100
+ <!-- Default GPU -->
101
+ <default_gpu>0</default_gpu>
102
+ <!-- Additional available GPUs -->
103
+ <additional_available_gpus>
104
+ <gpu>1</gpu>
105
+ <gpu>2</gpu>
106
+ <gpu>3</gpu>
107
+ </additional_available_gpus>
108
+ </gpu>
109
+
110
+ <!-- Search space information -->
111
+ <search_space write_debug_info="false">
112
+ <!-- if the user wants to use custom HRF, pRF sizes or Spatial grid, provide the filepath to the H5 file and the keys for each parameter -->
113
+ <optional_analysis_params enable="False" filepath="DIR_PATH/analysis_additional_params.h5">
114
+ <hrf use_from_file="True" key="analysis_params/hrf_values"/>
115
+ <sigmas use_from_file="True" key="analysis_params/sigmas"/>
116
+ <spatial_grid_xy use_from_file="True" key="analysis_params/spatial_grid_xy"/>
117
+ </optional_analysis_params>
118
+
119
+ <!-- if the user wants to generate a rectilinear grid within the GEM-pRF program, specify the default values below. Please NOTE that these values will ONLY be used if their corresponding optional_analysis_params = False -->
120
+ <default_hrf t="(0, 45)" TR="auto" peak_delay="6.16" under_shoot_delay="12.0" peak_disp="0.85" under_disp="0.82" peak_to_undershoot="2.15" normalize="true"/> <!-- SPM parameters, "t" is fed to np.arange(start, stop, step), "step" represents the TR value, set it to a lower value in case of high temporal stimulus. If TR = "auto" then, its value will be read from the stimulus file -->
121
+ <default_spatial_grid visual_field_radius="13.5" num_horizontal_prfs="151" num_vertical_prfs="151"/>
122
+ <default_sigmas num_sigmas="16" min_sigma="0.5" max_sigma="5"/>
123
+ </search_space>
124
+ </root>
@@ -0,0 +1,174 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ "@Author : Siddharth Mittal",
4
+ "@Version : 1.0",
5
+ "@Contact : siddharth.mittal@meduniwien.ac.at",
6
+ "@License : (C)Copyright 2023-2025, Medical University of Vienna",
7
+ "@Desc : None",
8
+
9
+ """
10
+
11
+ import json
12
+ import os
13
+ from enum import Enum
14
+ import xmltodict
15
+
16
+ class RunType(Enum):
17
+ ANALYSIS = 1
18
+ SIMULATION = 2
19
+
20
+ class ConfigurationWrapper:
21
+ config_data = None
22
+
23
+ @classmethod
24
+ def __read_xml_file(cls, run_type: RunType = None, config_filepath : str = None):
25
+ script_dir = os.path.dirname(os.path.realpath(__file__)) # Get the directory of the script
26
+
27
+ if config_filepath is not None:
28
+ config_file_path = config_filepath
29
+ else:
30
+ config_file_path = os.path.join(script_dir, "default_config", "default_config.xml") # default config file path
31
+
32
+ if os.path.isfile(config_file_path):
33
+ with open(config_file_path) as xml_file:
34
+ config_data_dict = xmltodict.parse(xml_file.read())
35
+ xml_file.close()
36
+
37
+ # XML to JSON data conversion
38
+ str_config_data = json.dumps(config_data_dict)
39
+ json_config_data = json.loads(str_config_data)
40
+ cls.config_data = json_config_data.get("root")
41
+ else:
42
+ print("Config file not found. Make sure it's located in the same directory as the script.")
43
+
44
+ @classmethod
45
+ def parse_attrs(cls, node, types=None):
46
+ """Extracts attributes (removes '@'), converts values using types dict if provided."""
47
+ if node is None:
48
+ return {}
49
+ types = types or {}
50
+ return {
51
+ k.lstrip("@"): types.get(k.lstrip("@"), str)(v)
52
+ for k, v in node.items()
53
+ if k.startswith("@")
54
+ }
55
+
56
+ @classmethod
57
+ def load_configuration(cls, run_type: RunType = None, config_filepath : str = None):
58
+ # if cls.config_data is None: #NOTE: This sometimes lead to unpredictable results, to be on safe side, always load the configuration
59
+ cls.__read_xml_file(run_type, config_filepath)
60
+ # # cls._read_json_file(run_type)
61
+
62
+ # path_to_append
63
+ cls.path_to_append = cls.config_data.get("path_to_append")
64
+
65
+ # Input data source
66
+ # ...BIDS
67
+ cls.bids = (cls.config_data.get("input_datasrc")).get("BIDS") # sample: {'@enable': 'True', 'basepath': 'D:/results/fmri/generic/', 'analysis': None, 'sub': None, 'ses': None}
68
+ # ...Non-BIDS i.e. direct filepaths
69
+ cls.fixed_paths = (cls.config_data.get("input_datasrc")).get("fixed_paths")
70
+
71
+ # result_file_path
72
+ cls.result_file_path = cls.config_data.get("result_file_path")
73
+
74
+ # results
75
+ #cls.results = cls.config_data.get("results")
76
+ cls.results = (cls.config_data.get("input_datasrc")).get("fixed_paths").get("results")
77
+
78
+ # PRF Model
79
+ cls.pRF_model_details = cls.config_data.get("pRF_model")
80
+
81
+ # Stimulus
82
+ cls.stimulus = cls.config_data.get("stimulus")
83
+ # ...subnodes of stimulus
84
+ high_temp_res_node = cls.stimulus.get("high_temporal_resolution", {})
85
+ cls.stimulus_high_temporal_resolution = cls.parse_attrs(high_temp_res_node, {
86
+ "enable": lambda v: v.lower() == "true",
87
+ "num_frames_downsampled": int,
88
+ "slice_time_ref": float
89
+ })
90
+
91
+ # Measure Data
92
+ cls.measured_data = cls.config_data.get("measured_data")
93
+
94
+ # Search Space
95
+ cls.search_space = cls.config_data.get("search_space")
96
+
97
+ # ...whether to write debug info or not
98
+ search_space_attrs = cls.parse_attrs(cls.search_space, {
99
+ "write_debug_info": lambda v: v.lower() == "true"
100
+ })
101
+ cls.write_debug_info = search_space_attrs.get("write_debug_info", False)
102
+
103
+ # Concatenated runs
104
+ cls.concatenated_runs = cls.config_data.get("concatenated_runs")
105
+
106
+ # Test Space
107
+ cls.test_space = cls.config_data.get("test_space")
108
+
109
+ # Noise
110
+ cls.noise = cls.config_data.get("noise")
111
+
112
+ # GPU
113
+ cls.gpu = cls.config_data.get("gpu")
114
+
115
+ # Enable/disable Refine fitting
116
+ refine_fitting_node_params = cls.parse_attrs(cls.config_data.get("refine_fitting", {}), {
117
+ "enable": lambda v: v.lower() == "true",
118
+ "refinefit_on_gpu": lambda v: v.lower() == "true"
119
+ })
120
+ cls.refine_fitting_enabled = refine_fitting_node_params['enable']
121
+ cls.is_refinefit_on_gpu = refine_fitting_node_params['refinefit_on_gpu']
122
+
123
+ # Optional analysis params
124
+ opt_node = cls.search_space.get("optional_analysis_params", {})
125
+ cls.optional_analysis_params = cls.parse_attrs(opt_node, {
126
+ "enable": lambda v: v.lower() == "true",
127
+ "filepath": str
128
+ })
129
+
130
+ # ...subnodes of optional_analysis_params
131
+ for key in ["hrf", "sigmas", "spatial_grid_xy"]:
132
+ subnode = opt_node.get(key, {}) # <--- get from original XML dict
133
+ cls.optional_analysis_params[key] = cls.parse_attrs(subnode, {
134
+ "use_from_file": lambda v: v.lower() == "true",
135
+ "key": str
136
+ })
137
+
138
+ # default spatial grid
139
+ cls.default_spatial_grid = cls.parse_attrs(cls.search_space.get("default_spatial_grid"), {
140
+ "visual_field_radius": float,
141
+ "num_horizontal_prfs": int,
142
+ "num_vertical_prfs": int
143
+ })
144
+
145
+ # default sigmas
146
+ cls.default_sigmas = cls.parse_attrs(cls.search_space.get("default_sigmas"), {
147
+ "num_sigmas": int,
148
+ "min_sigma": float,
149
+ "max_sigma": float
150
+ })
151
+
152
+ # default hrf
153
+ cls.default_hrf = cls.parse_attrs(cls.search_space.get("default_hrf"), {
154
+ "t": lambda v: tuple(map(float, v.strip("()").split(","))), # expects a string like "(0, 45)"
155
+ "TR": lambda v: float(v) if v.replace('.', '', 1).isdigit() else None,
156
+ "peak_delay": float,
157
+ "under_shoot_delay": float,
158
+ "peak_disp": float,
159
+ "under_disp": float,
160
+ "peak_to_undershoot": float,
161
+ "normalize": lambda v: v.lower() == "true"
162
+ })
163
+
164
+ print("Successfully read the config file. ")
165
+ if cls.optional_analysis_params['enable']:
166
+ print(f"Analysis params from file: {', '.join(k for k,v in cls.optional_analysis_params.items() if isinstance(v, dict) and v.get('use_from_file', False)) or 'None'}")
167
+
168
+ # Usage:
169
+ if __name__ == "__main__":
170
+ ConfigurationWrapper.load_configuration(RunType.ANALYSIS)
171
+
172
+ # Access the configuration parameters using the class itself
173
+ print(f"Visual Field Size: {ConfigurationWrapper.path_to_appended}")
174
+ print(f"Stimulus: {ConfigurationWrapper.stimulus}")
@@ -0,0 +1,141 @@
1
+ <!-- XML representation of JSON data -->
2
+ <!--
3
+ "@Author : Siddharth Mittal",
4
+ "@Version : 1.0",
5
+ "@Contact : siddharth.mittal@meduniwien.ac.at",
6
+ "@License : (C)Copyright 2024, Medical University of Vienna"
7
+ -->
8
+
9
+ <!-- NOTE for Analysis: For DUMMY/Analsis, set correct Visual field at two positions, one for the <stimulus> and another for <search_space>-->
10
+
11
+
12
+ <root>
13
+ <!-- Path information -->
14
+ <path_to_append>/ceph/mri.meduniwien.ac.at/departments/physics/fmrilab/home/smittal/dgx-versions/fmri/</path_to_append>
15
+
16
+ <!-- Input Data -->
17
+ <input_datasrc>
18
+ <!-- either spcify the BIDS info and set enable=True -->
19
+ <BIDS enable="False"> <!-- Flag to indicate BIDS format -->
20
+ <basepath>D:/results/gem-paper-simulated-data/analysis/08/BIDS/</basepath>
21
+ <append_to_basepath>derivatives, prfsynth</append_to_basepath> <!-- specify if the "analysis-xx" -->
22
+ <!-- <basepath>D:/results/fmri/generic/</basepath> -->
23
+
24
+ <!--
25
+ NOTE: Case sensitive, provide the exact values!!!
26
+ For the analysis, sub, ses, task, run, hemi,
27
+ either provide comma separated values or specify "all" (to go through every available analysis)
28
+ Sample: <analysis>01, 02</analysis> or <analysis>all</analysis>
29
+ -->
30
+ <analysis>08</analysis>
31
+ <sub>HighNoise</sub>
32
+ <ses>0n0, 3n3, 6n6</ses>
33
+ <task>prf</task>
34
+ <run>all</run>
35
+ <hemi>all</hemi>
36
+ <results_anaylsis_id>01</results_anaylsis_id> <!-- analysis id to save the results -->
37
+ </BIDS>
38
+
39
+ <!--OR, make Bids False and specify directly single/multiple filepaths -->
40
+ <fixed_paths>
41
+ <measured_data_filepath> <!-- Measured input data filepaths -->
42
+ <filepath>Y:\data\tests\gem-paper-simulated-data\analysis\04\BIDS\derivatives\prfsynth\analysis-04\sub-001\ses-3n2\func\sub-001_ses-3n2_task-prf_acq-normal_run-01_bold.nii.gz</filepath>
43
+ <filepath>...dummy_filepath.....</filepath>
44
+ </measured_data_filepath>
45
+ <results> <!-- Results filepaths realted -->
46
+ <basepath>D:\results\fmri/generic/</basepath> <!-- Base path for results -->
47
+ <custom_filename_postfix>_[gem-benchmark_51x51x8]</custom_filename_postfix> <!-- Custom filename postfix -->
48
+ <prepend_date>True</prepend_date> <!-- Flag to prepend date -->
49
+ </results>
50
+ </fixed_paths>
51
+ </input_datasrc>
52
+
53
+ <!-- Analysis Model -->
54
+ <pRF_model_details>
55
+ <model>2d_gaussian</model> <!-- 2d_gauss, DoG, CSS -->
56
+ </pRF_model_details>
57
+
58
+
59
+
60
+ <!-- Stimulus data -->
61
+ <stimulus>
62
+ <!-- File path for stimulus -->
63
+ <filepath>Y:\data\tests\gem-paper-simulated-data\analysis\04\BIDS\stimuli\sub-001_ses-3n2_task-prf_apertures.nii.gz</filepath>
64
+ <visual_field>10</visual_field> <!-- Visual field Radius, NOTE: Use "10" for simulated data, else "9" -->
65
+ <width>101</width>
66
+ <height>101</height>
67
+ </stimulus>
68
+
69
+ <!-- Measured data section -->
70
+ <measured_data>
71
+ <width>101</width>
72
+ <height>101</height>
73
+ <batches>16</batches>
74
+ </measured_data>
75
+
76
+ <!-- GPU configuration -->
77
+ <gpu>
78
+ <!-- Default GPU -->
79
+ <default_gpu>0</default_gpu>
80
+ <!-- Additional available GPUs -->
81
+ <additional_available_gpus>
82
+ <gpu>0</gpu>
83
+ <gpu>1</gpu>
84
+ <gpu>3</gpu>
85
+ </additional_available_gpus>
86
+ </gpu>
87
+
88
+ <!-- Search space information -->
89
+ <search_space>
90
+ <visual_field>13.5</visual_field> <!-- NOTE: use "9" for analysis with DUMMY signal because the signal is generated using maxEcc=9 ....and the visual field is 13.5 degrees for NORMAL analysis.-->
91
+ <nRows>51</nRows>
92
+ <nCols>51</nCols>
93
+ <min_sigma>0.5</min_sigma>
94
+ <max_sigma>5</max_sigma>
95
+ <nSigma>8</nSigma>
96
+ </search_space>
97
+
98
+ <!-- Test space information -->
99
+ <test_space>
100
+ <nRows>11</nRows>
101
+ <nCols>11</nCols>
102
+ <min_sigma>0.5</min_sigma>
103
+ <max_sigma>4</max_sigma>
104
+ <nSigma>1</nSigma>
105
+ </test_space>
106
+
107
+ <!-- Concatenated runs information -->
108
+ <concatenated_runs>
109
+ <enable>True</enable>
110
+ <num_runs>2</num_runs>
111
+ <height>101</height>
112
+ <batches>4</batches>
113
+
114
+ <!-- BIDS run information -->
115
+ <bids_runs>
116
+ <run>01</run>
117
+ <run>02</run>
118
+ </bids_runs>
119
+ <!-- BIDS task information -->
120
+ <bids_tasks>
121
+ <task>bar</task>
122
+ <task>ring</task>
123
+ </bids_tasks>
124
+ <!-- Stimulus file paths -->
125
+ <stimulus>
126
+ <filepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/stimsim23/derivatives/prfprepare/analysis-03/sub-sidtest/stimuli/task-bar_apertures.nii.gz</filepath>
127
+ <filepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/stimsim23/derivatives/prfprepare/analysis-03/sub-sidtest/stimuli/task-bar_apertures.nii.gz</filepath>
128
+ </stimulus>
129
+ <!-- Measured data file paths -->
130
+ <measured_data>
131
+ <filepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/stimsim23/derivatives/prfprepare/analysis-03/sub-sidtest/ses-001/func/sub-sidtest_ses-001_task-bar_run-01_hemi-L_bold.nii.gz</filepath>
132
+ </measured_data>
133
+ </concatenated_runs>
134
+
135
+ <!-- Noise information -->
136
+ <noise>
137
+ <level>0.1</level>
138
+ <model>gaussian</model>
139
+ <synthesis_ratio>1</synthesis_ratio> <!-- number of noisy signals per pRF location -->
140
+ </noise>
141
+ </root>