diff --git a/CPAC/utils/create_flame_model_files.py b/CPAC/utils/create_flame_model_files.py
index d77ebaaf5e..58f5be3b2b 100644
--- a/CPAC/utils/create_flame_model_files.py
+++ b/CPAC/utils/create_flame_model_files.py
@@ -1,3 +1,24 @@
+# Copyright (C) 2016-2024 C-PAC Developers
+
+# This file is part of C-PAC.
+
+# C-PAC is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+
+# C-PAC is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+# License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with C-PAC. If not, see .
+from CPAC.utils.monitoring.custom_logging import getLogger
+
+logger = getLogger("nipype.workflow")
+
+
def create_dummy_string(length):
ppstring = ""
for i in range(0, length):
@@ -126,6 +147,8 @@ def create_fts_file(ftest_list, con_names, model_name, current_output, out_dir):
import numpy as np
+ logger.info("\nFound f-tests in your model, writing f-tests file (.fts)..\n")
+
try:
out_file = os.path.join(out_dir, model_name + ".fts")
@@ -202,7 +225,8 @@ def create_con_ftst_file(
evs = evs.rstrip("\r\n").split(",")
if evs[0].strip().lower() != "contrasts":
- raise Exception
+ msg = "first cell in contrasts file should contain 'Contrasts'"
+ raise ValueError(msg)
# remove "Contrasts" label and replace it with "Intercept"
# evs[0] = "Intercept"
@@ -217,7 +241,8 @@ def create_con_ftst_file(
try:
contrasts_data = np.genfromtxt(con_file, names=True, delimiter=",", dtype=None)
except:
- raise Exception
+ msg = f"Could not successfully read in contrast file: {con_file}"
+ raise OSError(msg)
lst = contrasts_data.tolist()
# lst = list of rows of the contrast matrix (each row represents a
@@ -291,27 +316,31 @@ def create_con_ftst_file(
fts_n = fts_columns.T
if len(column_names) != (num_EVs_in_con_file):
- "\n\n[!] CPAC says: The number of EVs in your model " "design matrix (found in the %s.mat file) does not " "match the number of EVs (columns) in your custom " "contrasts matrix CSV file.\n\nCustom contrasts matrix " "file: %s\n\nNumber of EVs in design matrix: %d\n" "Number of EVs in contrasts file: %d\n\nThe column " "labels in the design matrix should match those in " "your contrasts .CSV file.\nColumn labels in design " "matrix:\n%s" % (
+ logger.error(
+ "\n\n[!] CPAC says: The number of EVs in your model design matrix (found"
+ " in the %s.mat file) does not match the number of EVs (columns) in your"
+ " custom contrasts matrix CSV file.\n\nCustom contrasts matrix file:"
+ " %s\n\nNumber of EVs in design matrix: %d\nNumber of EVs in contrasts"
+ " file: %d\n\nThe column labels in the design matrix should match those in"
+ "your contrasts .CSV file.\nColumn labels in design matrix:\n%s",
model_name,
con_file,
len(column_names),
num_EVs_in_con_file,
str(column_names),
)
-
- # raise Exception(err_string)
return None, None
for design_mat_col, con_csv_col in zip(column_names, evs[1:]):
if con_csv_col not in design_mat_col:
- errmsg = (
- "\n\n[!] CPAC says: The names of the EVs in your "
- "custom contrasts .csv file do not match the names or "
- "order of the EVs in the design matrix. Please make "
- "sure these are consistent.\nDesign matrix EV columns: "
- "%s\nYour contrasts matrix columns: %s\n\n" % (column_names, evs[1:])
+ logger.error(
+ "\n\n[!] CPAC says: The names of the EVs in your custom contrasts .csv"
+ " file do not match the names or order of the EVs in the design"
+ " matrix. Please make sure these are consistent.\nDesign matrix EV"
+ " columns: %s\nYour contrasts matrix columns: %s\n\n",
+ column_names,
+ evs[1:],
)
-
return None, None
out_file = os.path.join(output_dir, model_name + ".con")
@@ -344,6 +373,7 @@ def create_con_ftst_file(
ftest_out_file = None
if fTest:
+ logger.info("\nFound f-tests in your model, writing f-tests file (.fts)..\n")
ftest_out_file = os.path.join(output_dir, model_name + ".fts")
with open(ftest_out_file, "wt") as f:
diff --git a/CPAC/utils/create_fsl_flame_preset.py b/CPAC/utils/create_fsl_flame_preset.py
index de99d3b3f1..4ac8474c88 100644
--- a/CPAC/utils/create_fsl_flame_preset.py
+++ b/CPAC/utils/create_fsl_flame_preset.py
@@ -1,3 +1,23 @@
+# Copyright (C) 2018-2024 C-PAC Developers
+
+# This file is part of C-PAC.
+
+# C-PAC is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+
+# C-PAC is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+# License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with C-PAC. If not, see .
+from CPAC.utils.monitoring.custom_logging import getLogger
+
+logger = getLogger("nipype.workflow")
+
# TODO: create a function that can help easily map raw pheno files that do not
# TODO: have the participant_session id that CPAC uses
@@ -56,7 +76,7 @@ def write_group_list_text_file(group_list, out_file=None):
f.write(f"{part_id}\n")
if os.path.exists(out_file):
- pass
+ logger.info("Group-level analysis participant list written:\n%s\n", out_file)
return out_file
@@ -83,7 +103,7 @@ def write_dataframe_to_csv(matrix_df, out_file=None):
matrix_df.to_csv(out_file, index=False)
if os.path.exists(out_file):
- pass
+ logger.info("CSV file written:\n%s\n", out_file)
def write_config_dct_to_yaml(config_dct, out_file=None):
@@ -170,7 +190,9 @@ def write_config_dct_to_yaml(config_dct, out_file=None):
)
if os.path.exists(out_file):
- pass
+ logger.info(
+ "Group-level analysis configuration YAML file written:\n%s\n", out_file
+ )
def create_design_matrix_df(
diff --git a/CPAC/utils/create_fsl_model.py b/CPAC/utils/create_fsl_model.py
index 9faff76984..f11b456e58 100644
--- a/CPAC/utils/create_fsl_model.py
+++ b/CPAC/utils/create_fsl_model.py
@@ -1,3 +1,24 @@
+# Copyright (C) 2012-2024 C-PAC Developers
+
+# This file is part of C-PAC.
+
+# C-PAC is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+
+# C-PAC is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+# License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with C-PAC. If not, see .
+from CPAC.utils.monitoring.custom_logging import getLogger
+
+logger = getLogger("nipype.workflow")
+
+
def load_pheno_file(pheno_file):
import os
@@ -362,7 +383,13 @@ def model_group_var_separately(
grouping_var, formula, pheno_data_dict, ev_selections, coding_scheme
):
if grouping_var is None or grouping_var not in formula:
- raise Exception
+ msg = (
+ "\n\n[!] CPAC says: Model group variances separately is enabled, but the"
+ " grouping variable set is either set to None, or was not included in the"
+ f" model as one of the EVs.\n\nDesign formula: {formula}\nGrouping"
+ f" variable: {grouping_var}\n\n"
+ )
+ raise ValueError(msg)
# do this a little early for the grouping variable so that it doesn't
# get in the way of doing this for the other EVs once they have the
@@ -471,18 +498,33 @@ def model_group_var_separately(
def check_multicollinearity(matrix):
import numpy as np
+ logger.info("\nChecking for multicollinearity in the model..")
+
U, s, V = np.linalg.svd(matrix)
max_singular = np.max(s)
min_singular = np.min(s)
+ logger.info(
+ "Max singular: %s\nMin singular: %s\nRank: %s\n\n",
+ max_singular,
+ min_singular,
+ np.linalg.matrix_rank(matrix),
+ )
+
+ _warning = (
+ "[!] CPAC warns: Detected multicollinearity in the computed group-level"
+ " analysis model. Please double-check your model design.\n\n"
+ )
+
if min_singular == 0:
- pass
+ logger.warning(_warning)
else:
condition_number = float(max_singular) / float(min_singular)
+ logger.info("Condition number: %f\n\n", condition_number)
if condition_number > 30:
- pass
+ logger.warning(_warning)
def write_mat_file(
@@ -805,8 +847,17 @@ def create_design_matrix(
try:
dmatrix = patsy.dmatrix(formula, pheno_data_dict, NA_action="raise")
- except:
- raise Exception
+ except Exception as e:
+ msg = (
+ "\n\n[!] CPAC says: Design matrix creation wasn't successful - do the"
+ " terms in your formula correctly correspond to the EVs listed in your"
+ " phenotype file?\nPhenotype file provided: %s\n\nPhenotypic data"
+ " columns (regressors): %s\nFormula: %s\n\n",
+ pheno_file,
+ list(pheno_data_dict.keys()),
+ formula,
+ )
+ raise RuntimeError(msg) from e
# check the model for multicollinearity - Patsy takes care of this, but
# just in case
@@ -976,6 +1027,8 @@ def create_dummy_string(length):
def create_con_file(con_dict, col_names, file_name, current_output, out_dir):
import os
+ logger.info("col names: %s", col_names)
+
with open(os.path.join(out_dir, file_name) + ".con", "w+") as f:
# write header
num = 1
@@ -1012,6 +1065,7 @@ def create_fts_file(ftest_list, con_dict, model_name, current_output, out_dir):
import numpy as np
try:
+ logger.info("\nFound f-tests in your model, writing f-tests file (.fts)..\n")
with open(os.path.join(out_dir, model_name + ".fts"), "w") as f:
print("/NumWaves\t", len(con_dict), file=f)
print("/NumContrasts\t", len(ftest_list), file=f)
@@ -1089,6 +1143,7 @@ def create_con_ftst_file(
# evs[0] = "Intercept"
fTest = False
+ logger.info("evs: %s", evs)
for ev in evs:
if "f_test" in ev:
count_ftests += 1
@@ -1099,8 +1154,9 @@ def create_con_ftst_file(
try:
data = np.genfromtxt(con_file, names=True, delimiter=",", dtype=None)
- except:
- raise Exception
+ except Exception as e:
+ msg = f"Error: Could not successfully read in contrast file: {con_file}"
+ raise OSError(msg) from e
lst = data.tolist()
@@ -1218,6 +1274,7 @@ def create_con_ftst_file(
np.savetxt(f, contrasts, fmt="%1.5e", delimiter="\t")
if fTest:
+ logger.info("\nFound f-tests in your model, writing f-tests file (.fts)..\n")
ftest_out_dir = os.path.join(output_dir, model_name + ".fts")
with open(ftest_out_dir, "wt") as f:
@@ -1361,8 +1418,13 @@ def run(
try:
if not os.path.exists(output_dir):
os.makedirs(output_dir)
- except:
- raise Exception
+ except Exception as e:
+ msg = (
+ "\n\n[!] CPAC says: Could not successfully create the group analysis"
+ f" output directory:\n{output_dir}\n\nMake sure you have write access"
+ " in this file structure.\n\n\n"
+ )
+ raise OSError(msg) from e
measure_dict = {}
@@ -1551,6 +1613,10 @@ def run(
or (custom_contrasts == "")
or ("None" in custom_contrasts)
):
+ logger.info(
+ "Writing contrasts file (.con) based on contrasts provided using the group"
+ " analysis model builder's contrasts editor.."
+ )
create_con_file(
contrasts_dict, regressor_names, model_name, current_output, model_out_dir
)
@@ -1561,6 +1627,11 @@ def run(
)
else:
+ logger.info(
+ "\nWriting contrasts file (.con) based on contrasts provided with a custom"
+ " contrasts matrix CSV file..\n"
+ )
+
create_con_ftst_file(
custom_contrasts,
model_name,