Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
68e3cb5
ENH: Refactor BrainAtlas class to remove hardcoded atlas data and imp…
acsenrafilho Aug 15, 2025
29aae31
ENH: Update set_image method to accept Union types and adjust test as…
acsenrafilho Aug 16, 2025
8cef1dc
ENH: Refactor isotropic Gaussian and median functions to use clone_im…
acsenrafilho Aug 16, 2025
1bb2af0
STY: Improve code readability by formatting long lines in MultiTE_ASL…
acsenrafilho Aug 16, 2025
56f055e
STY: Format long lines in CBFMapping and test cases for improved read…
acsenrafilho Aug 16, 2025
6a56818
ENH: Refactor ImageIO methods to improve image handling and add deep …
acsenrafilho Aug 16, 2025
1f3a6b6
ENH: Update calculate_snr, calculate_mean_intensity, and analyze_imag…
acsenrafilho Aug 16, 2025
90cbccd
ENH: Update image manipulation functions to use ImageIO objects; adju…
acsenrafilho Aug 16, 2025
8d1fa1f
ENH: Update test cases in test_smooth_utils to use ImageIO objects fo…
acsenrafilho Aug 16, 2025
03b0150
ENH: Refactor T2Scalar_ASLMapping to utilize ImageIO for brain mask h…
acsenrafilho Aug 16, 2025
c646f9c
ENH: Refactor MultiDW_ASLMapping methods to improve readability and m…
acsenrafilho Aug 16, 2025
87d58a4
ENH: Update functions to utilize ImageIO objects for image handling; …
acsenrafilho Aug 16, 2025
efec6c3
WIP: Refactor head_movement_correction to utilize ImageIO for referen…
acsenrafilho Aug 16, 2025
ffa39fb
Merge remote-tracking branch 'origin/main' into enh/image_io
acsenrafilho Aug 16, 2025
26ce7f1
ENH: Refactor tests to utilize ImageIO for image loading in registrat…
acsenrafilho Aug 18, 2025
6d8d6c1
WIP: Fixing registration (head_moviment)
acsenrafilho Aug 18, 2025
4d533ea
WIP: Adjusting more tests with ImageIO refactoring
acsenrafilho Aug 19, 2025
ceed790
WIP: ImageIO tests
acsenrafilho Aug 20, 2025
67df879
DOC: Enhance documentation for ImageIO class and its methods
acsenrafilho Aug 20, 2025
aac55c3
ENH: Refactor and enhance tests for ASL template registration, includ…
acsenrafilho Aug 20, 2025
f684c60
ENH: Improve test assertions for ImageIO and registration functions, …
acsenrafilho Aug 20, 2025
fecf1f5
STY: Improve code formatting and style in ImageIO class, enhancing re…
acsenrafilho Aug 20, 2025
ce3a458
ENH: Refactor and improve formatting of the collect_data_volumes and …
acsenrafilho Aug 20, 2025
c6d2d22
STY: Improve code formatting in isotropic_gaussian function for bette…
acsenrafilho Aug 20, 2025
ac4efbe
ENH: Refactor asl_template_registration function to improve atlas ref…
acsenrafilho Aug 20, 2025
ff8996f
ENH: Clean up space_normalization function by removing unused paramet…
acsenrafilho Aug 20, 2025
2f460d1
ENH: Optimize T2 map stacking and improve ImageIO object creation for…
acsenrafilho Aug 20, 2025
1ded438
ENH: Update brain mask handling in CBFMapping, MultiDW_ASLMapping, an…
acsenrafilho Aug 20, 2025
9b47011
ENH: Update ASLData methods to utilize get_as_numpy for improved data…
acsenrafilho Aug 20, 2025
d6e46df
ENH: BACKUP IF NEEDED Remove unused orientation check functions and r…
acsenrafilho Aug 20, 2025
a5dfc89
ENH: Add average_m0 option and refactor image loading to use ImageIO …
acsenrafilho Aug 20, 2025
ac85667
ENH: Add --average_m0 option and refactor image loading to use ImageI…
acsenrafilho Aug 20, 2025
8a5d11e
ENH: Add --average_m0 option and refactor image handling to improve c…
acsenrafilho Aug 20, 2025
caed32c
ENH: Add suppress_warnings option to create_map method for better con…
acsenrafilho Aug 20, 2025
5d26282
DOC: Improve commit message guidelines for clarity and organization
acsenrafilho Aug 20, 2025
4dc26c0
DOC: Update contribution guidelines links in pull request template fo…
acsenrafilho Aug 20, 2025
ed6ed57
DOC: Update contribution guidelines for clarity and detail
acsenrafilho Aug 20, 2025
0179b2b
DOC: Clarify contribution guidelines and encourage PRs for documentat…
acsenrafilho Aug 20, 2025
0c22bc9
STY: Remove logging statements from space_normalization function for …
acsenrafilho Aug 21, 2025
3b80959
ENH: Refactor ASLData initialization and improve warning suppression …
acsenrafilho Aug 21, 2025
17f01d5
ENH: Add step to move source to short path for Windows compatibility …
acsenrafilho Aug 21, 2025
9cc9cd3
ENH: Add ITK installation and configure additional dependencies for W…
acsenrafilho Aug 21, 2025
f6182f5
ENH: Upgrade Python version to 3.10 in CI workflow for all platforms
acsenrafilho Aug 21, 2025
118d5ed
ENH: Update Python version requirement to 3.10 in pyproject.toml
acsenrafilho Aug 21, 2025
df69399
ENH: Update Python version requirements and classifiers in pyproject.…
acsenrafilho Aug 21, 2025
90c3263
BUG: Update ImageIO string representation test to check for path pres…
acsenrafilho Aug 21, 2025
0ff79b9
ENH: Upgrade Python version to 3.10 in CI workflows and bumpversion p…
acsenrafilho Aug 21, 2025
0adb478
Merge pull request #75 from LOAMRI/enh/image_io
acsenrafilho Aug 21, 2025
2c38ebc
Refactor documentation and scripts in ASLtk
acsenrafilho Aug 21, 2025
5de8548
Merge branch 'develop' into doc/more_details
acsenrafilho Aug 21, 2025
7e9d523
Merge pull request #76 from LOAMRI/doc/more_details
acsenrafilho Aug 21, 2025
fab8af0
ENH: Add tests for BrainAtlas creation with various names and resolut…
acsenrafilho Aug 21, 2025
82ba9f4
ENH: Add resolution parameter to BrainAtlas initialization and method…
acsenrafilho Aug 21, 2025
bf51586
DOC: Update README with new Python version and add website badge
acsenrafilho Aug 21, 2025
3240a31
Merge pull request #79 from LOAMRI/issue-70
acsenrafilho Aug 21, 2025
bfe8ea1
ENH: Update TODO comments for clarity on data loading and reconstruct…
acsenrafilho Aug 22, 2025
352fc06
ENH: Improve error handling messages in head_movement_correction and …
acsenrafilho Aug 22, 2025
e94636c
ENH: Add suppress_warnings parameter to create_map method and impleme…
acsenrafilho Aug 22, 2025
b428747
Merge pull request #81 from LOAMRI/issue-63
acsenrafilho Aug 22, 2025
bbed2d7
ENH: Add unit tests for UltraLongTE_ASLMapping functionality and erro…
acsenrafilho Aug 22, 2025
a4dacb4
DOC: Update navigation in mkdocs.yml to reference script_methods.md d…
acsenrafilho Aug 22, 2025
aed717b
ENH: Implement UltraLongTE ASL mapping class with advanced multi-echo…
acsenrafilho Aug 22, 2025
ad13adb
ENH: Add get_optimal_core_count function to determine optimal CPU cor…
acsenrafilho Aug 22, 2025
b6f1f37
ENH: Update ASLData initialization in test_multi_dw_mapping and test_…
acsenrafilho Aug 22, 2025
f091a14
ENH: Add UltraLong-TE ASL mapping script with comprehensive argument …
acsenrafilho Aug 22, 2025
ff79aff
STY: Remove unused SimpleITK import from te_asl.py
acsenrafilho Aug 22, 2025
3ce3455
DOC: Update type annotations in MultiTE_ASLMapping methods to reflect…
acsenrafilho Aug 22, 2025
d281026
ENH: Update default CPU core count in CBFMapping.create_map to use ha…
acsenrafilho Aug 22, 2025
a477bfb
ENH: Add UltraLongTE_ASLMapping to the module exports
acsenrafilho Aug 22, 2025
0da9d6f
STY: Fix lint
acsenrafilho Aug 22, 2025
6aa8ddb
Merge pull request #82 from LOAMRI/issue-65
acsenrafilho Aug 22, 2025
dfa859e
ENH: Update CI workflow to support Python versions 3.10, 3.11, 3.12, …
acsenrafilho Aug 22, 2025
ef61834
ENH: Add error handling for Kaggle API rate limit in BrainAtlas tests
acsenrafilho Aug 22, 2025
7482f17
ENH: Improve error handling for Kaggle API rate limit in BrainAtlas t…
acsenrafilho Aug 22, 2025
f8c37c3
ENH: Implement rate limiting for API calls in BrainAtlas class to pre…
acsenrafilho Aug 22, 2025
8ce6cce
ENH: Add pre-commit configuration for linting tasks and update depend…
acsenrafilho Aug 22, 2025
3b82f52
ENH: Reduce the number of Python version to only 3.10 in main workflow
acsenrafilho Aug 22, 2025
99d5977
DOC: Update contribution guidelines to include pre-commit hook instal…
acsenrafilho Aug 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
- Use supported image formats: `.nii`, `.nii.gz`, `.mha`, `.nrrd`.
- Ensure that the code is syntactically correct and adheres to the project's coding standards.
- Be sure about the documentation and comments. They should be clear and concise and use the correct Python docstring format.
- Create commit messages with a detailed description of the changes made, including any bug fixes or new features.
- Be as much specific as possible in the commit messages, including the files affected and the nature of the changes.
- Create commit messages with a as much details and description as possible in order to explain all the relevant information about the changes made, including any bug fixes or new features.
- Be as much specific and complete as possible in the commit messages, including the files affected and the nature of the changes.
- Organize the commit messages in bullet points for better readability, showing all the relevant information that is needed to explain the changes made.
- Uses for commit messages prefixes the following pattern:
- `ENH:` for new features and code enhancements
- `BUG:` for bug fixes and general corrections
Expand Down
6 changes: 3 additions & 3 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ Please select the relevant option(s):
- [ ] Code passes linting checks (`task lint-check`)
- [ ] Code includes appropriate inline documentation

**Note:** According to our [contribution guidelines](docs/contribute.md), please target the `develop` branch for most contributions to ensure proper testing before merging to `main`.
**Note:** According to our [contribution guidelines](https://asltk.readthedocs.io/en/main/contribute/), please target the `develop` branch for most contributions to ensure proper testing before merging to `main`.

**By submitting this pull request, I confirm:**
- [ ] I have read and followed the [contribution guidelines](docs/contribute.md)
- [ ] I have read and followed the [contribution guidelines](https://asltk.readthedocs.io/en/main/contribute/)
- [ ] I have tested my changes thoroughly
- [ ] I understand this is an open source project and my contributions may be used by others
- [ ] I agree to the project's [Code of Conduct](CODE_OF_CONDUCT.md)
- [ ] I agree to the project's [Code of Conduct](https://github.com/LOAMRI/asltk/blob/main/CODE_OF_CONDUCT.md)

<!-- Thank you for contributing to asltk! πŸš€ -->
2 changes: 1 addition & 1 deletion .github/workflows/bumpversion_publish_workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.9'
python-version: '3.10'

- name: Install Poetry
run: pip install poetry
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci_develop.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]


steps:
Expand Down Expand Up @@ -54,7 +54,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]

steps:
- name: Clone repo
Expand Down Expand Up @@ -106,7 +106,7 @@ jobs:
runs-on: macos-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]

steps:
- name: Clone repo
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci_main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]


steps:
Expand Down Expand Up @@ -53,7 +53,7 @@ jobs:
runs-on: windows-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]

steps:
- name: Clone repo
Expand Down Expand Up @@ -105,7 +105,7 @@ jobs:
runs-on: macos-latest
strategy:
matrix:
python-version: ["3.9"]
python-version: ["3.10"]

steps:
- name: Clone repo
Expand Down
9 changes: 9 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
repos:
- repo: local
hooks:
- id: asltk-lint
name: Run asltk lint task
entry: poetry run task lint
language: system
pass_filenames: false
types: [python]
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<img src="https://raw.githubusercontent.com/LOAMRI/asltk/refs/heads/develop/docs/assets/asltk-logo.png" width=200>

[![Documentation Stable](https://readthedocs.org/projects/asltk/badge/?version=main)](https://asltk.readthedocs.io/en/main/?badge=main)
![Website](https://img.shields.io/website?url=https%3A%2F%2Fasltk.readthedocs.io%2Fen%2Fmain%2F&up_message=asltk%20documentation&link=https%3A%2F%2Fasltk.readthedocs.io%2Fen%2Fmain%2F)
[![codecov](https://codecov.io/gh/LOAMRI/asltk/graph/badge.svg?token=1W8GQ7SLU9)](https://codecov.io/gh/LOAMRI/asltk)
[![CI_main](https://github.com/LOAMRI/asltk/actions/workflows/ci_main.yaml/badge.svg)](https://github.com/LOAMRI/asltk/actions/workflows/ci_main.yaml)
[![CI_develop](https://github.com/LOAMRI/asltk/actions/workflows/ci_develop.yaml/badge.svg)](https://github.com/LOAMRI/asltk/actions/workflows/ci_develop.yaml)
![Python Versions](https://img.shields.io/badge/python-3.9%20|+-blue)
![Python Versions](https://img.shields.io/badge/python-3.10%20|+-blue)
[![PyPI downloads](https://img.shields.io/pypi/dm/asltk?label=PyPI%20downloads)](https://pypi.org/project/asltk/)
![Contributors](https://img.shields.io/github/contributors/LOAMRI/asltk)
[![GitHub issues](https://img.shields.io/github/issues-raw/LOAMRI/asltk.svg?maxAge=2592000)]()
Expand Down
68 changes: 46 additions & 22 deletions asltk/asldata.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import copy
import os
import warnings
from typing import Union

import numpy as np

from asltk.logging_config import get_logger, log_data_info, log_function_call
from asltk.logging_config import get_logger, log_data_info
from asltk.utils.image_manipulation import collect_data_volumes
from asltk.utils.io import load_image
from asltk.utils.io import ImageIO


class ASLData:
Expand Down Expand Up @@ -69,40 +70,53 @@ def __init__(
if isinstance(kwargs.get('pcasl'), str):
pcasl_path = kwargs.get('pcasl')
logger.info(f'Loading ASL image from: {pcasl_path}')
self._asl_image = load_image(pcasl_path)
self._asl_image = ImageIO(image_path=pcasl_path)
if self._asl_image is not None:
log_data_info(
'ASL image', self._asl_image.shape, pcasl_path
'ASL image',
self._asl_image.get_as_numpy().shape,
pcasl_path,
)
elif isinstance(kwargs.get('pcasl'), np.ndarray):
self._asl_image = kwargs.get('pcasl')
logger.info('ASL image loaded as numpy array')
self._asl_image = ImageIO(image_array=kwargs.get('pcasl'))
logger.info('ASL image loaded')
log_data_info(
'ASL image', self._asl_image.shape, 'numpy array'
'ASL image', self._asl_image.get_as_numpy().shape
)

if kwargs.get('m0') is not None:
average_m0 = kwargs.get('average_m0', False)

if isinstance(kwargs.get('m0'), str):
m0_path = kwargs.get('m0')
logger.info(f'Loading M0 image from: {m0_path}')
self._m0_image = load_image(m0_path)
self._m0_image = ImageIO(
image_path=m0_path, average_m0=average_m0
)

# Check if M0 image is 4D and warn if so
if (
self._m0_image is not None
and len(self._m0_image.shape) > 3
and len(self._m0_image.get_as_numpy().shape) > 3
):
warnings.warn('M0 image has more than 3 dimensions.')

if self._m0_image is not None:
log_data_info('M0 image', self._m0_image.shape, m0_path)
log_data_info(
'M0 image',
self._m0_image.get_as_numpy().shape,
m0_path,
)
elif isinstance(kwargs.get('m0'), np.ndarray):
self._m0_image = kwargs.get('m0')
self._m0_image = ImageIO(
image_array=kwargs.get('m0'), average_m0=average_m0
)
logger.info('M0 image loaded as numpy array')
log_data_info('M0 image', self._m0_image.shape, 'numpy array')

if kwargs.get('average_m0', False):
self._m0_image = np.mean(self._m0_image, axis=0)
log_data_info(
'M0 image',
self._m0_image.get_as_numpy().shape,
'numpy array',
)

self._parameters['ld'] = (
[] if kwargs.get('ld_values') is None else kwargs.get('ld_values')
Expand Down Expand Up @@ -133,8 +147,8 @@ def __init__(

logger.debug('ASLData object created successfully')

def set_image(self, image, spec: str):
"""Insert a image necessary to define de ASL data processing.
def set_image(self, image: Union[str, np.ndarray], spec: str, **kwargs):
"""Insert an image necessary to define the ASL data processing.

The `spec` parameters specifies what is the type of image to be used in
ASL processing step. Choose one of the options: `m0` for the M0 volume,
Expand All @@ -152,7 +166,7 @@ def set_image(self, image, spec: str):
>>> data = ASLData()
>>> path_m0 = './tests/files/m0.nii.gz' # M0 file with shape (5,35,35)
>>> data.set_image(path_m0, spec='m0')
>>> data('m0').shape
>>> data('m0').get_as_numpy().shape
(5, 35, 35)

Args:
Expand All @@ -161,10 +175,18 @@ def set_image(self, image, spec: str):
"""
if isinstance(image, str) and os.path.exists(image):
if spec == 'm0':
self._m0_image = load_image(image)
self._m0_image = ImageIO(image, **kwargs)
elif spec == 'pcasl':
self._asl_image = load_image(image)
self._asl_image = ImageIO(image, **kwargs)
elif isinstance(image, np.ndarray):
warnings.warn(
'Using numpy array as image input does not preserve metadata or image properties.'
)
if spec == 'm0':
self._m0_image = ImageIO(image_array=image, **kwargs)
elif spec == 'pcasl':
self._asl_image = ImageIO(image_array=image, **kwargs)
elif isinstance(image, ImageIO):
if spec == 'm0':
self._m0_image = image
elif spec == 'pcasl':
Expand Down Expand Up @@ -277,9 +299,11 @@ def __call__(self, spec: str):
Examples:
>>> data = ASLData(pcasl='./tests/files/t1-mri.nrrd')
>>> type(data('pcasl'))
<class 'asltk.utils.io.ImageIO'>
>>> type(data('pcasl').get_as_numpy())
<class 'numpy.ndarray'>

>>> np.min(data('pcasl'))
>>> np.min(data('pcasl').get_as_numpy())
0

Returns:
Expand Down Expand Up @@ -327,7 +351,7 @@ def _check_ld_pld_sizes(self, ld, pld):
)

def _check_m0_dimension(self):
if len(self._m0_image.shape) > 3:
if len(self._m0_image.get_as_numpy().shape) > 3:
warnings.warn(
'M0 image has more than 3 dimensions. '
'This may cause issues in processing. '
Expand Down
77 changes: 68 additions & 9 deletions asltk/aux_methods.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
import warnings
from multiprocessing import cpu_count
from typing import Any, Dict, Optional

import numpy as np
import psutil

from asltk.smooth import isotropic_gaussian, isotropic_median
from asltk.utils.io import ImageIO


def _check_mask_values(mask, label, ref_shape):
# Check wheter mask input is an numpy array
if not isinstance(mask, np.ndarray):
raise TypeError(f'mask is not an numpy array. Type {type(mask)}')
def _check_mask_values(mask: ImageIO, label, ref_shape):
"""Validate mask array for brain mask processing.

This function performs comprehensive validation of brain mask data to ensure
it meets the requirements for ASL processing. It checks data type, binary
format compliance, label presence, and dimensional compatibility.

Args:
mask (np.ndarray): The brain mask image to validate.
label (int or float): The label value to search for in the mask.
ref_shape (tuple): The reference shape that the mask should match.

Raises:
TypeError: If mask is not a numpy array or dimensions don't match.
ValueError: If the specified label value is not found in the mask.

Warnings:
UserWarning: If mask contains more than 2 unique values (not strictly binary).
"""
# Check wheter mask input is an ImageIO object
if not isinstance(mask, ImageIO):
raise TypeError(
f'mask is not an ImageIO object. Type {type(mask)} is not allowed.'
)

mask_array = mask.get_as_numpy()

# Check whether the mask provided is a binary image
unique_values = np.unique(mask)
unique_values = np.unique(mask_array)
if unique_values.size > 2:
warnings.warn(
'Mask image is not a binary image. Any value > 0 will be assumed as brain label.',
Expand All @@ -29,18 +54,18 @@ def _check_mask_values(mask, label, ref_shape):
raise ValueError('Label value is not found in the mask provided.')

# Check whether the dimensions between mask and input volume matches
mask_shape = mask.shape
mask_shape = mask_array.shape
if mask_shape != ref_shape:
raise TypeError(
f'Image mask dimension does not match with input 3D volume. Mask shape {mask_shape} not equal to {ref_shape}'
)


def _apply_smoothing_to_maps(
maps: Dict[str, np.ndarray],
maps: Dict[str, ImageIO],
smoothing: Optional[str] = None,
smoothing_params: Optional[Dict[str, Any]] = None,
) -> Dict[str, np.ndarray]:
) -> Dict[str, ImageIO]:
"""Apply smoothing filter to all maps in the dictionary.

This function applies the specified smoothing filter to all map arrays
Expand Down Expand Up @@ -117,7 +142,7 @@ def _apply_smoothing_to_maps(
# Apply smoothing to all maps
smoothed_maps = {}
for key, map_array in maps.items():
if isinstance(map_array, np.ndarray):
if isinstance(map_array, ImageIO):
try:
smoothed_maps[key] = smooth_func(map_array, **smoothing_params)
except Exception as e:
Expand All @@ -132,3 +157,37 @@ def _apply_smoothing_to_maps(
smoothed_maps[key] = map_array

return smoothed_maps


def get_optimal_core_count(
requested_cores: int = None, mb_per_core: int = 500
):
"""Determine optimal number of cores based on available memory.

This function calculates the appropriate number of CPU cores to use for
parallel processing based on the available system memory. It ensures
that the process won't exhaust the system's memory during computation.

This implementation is OS-agnostic and works consistently across
Windows, Linux, and macOS platforms.

Args:
requested_cores (int or str, optional): User-requested number of cores.
If an integer and > 0, uses this value (capped by system limits).
If "auto" or None, calculates based on available memory.
mb_per_core (int, optional): Memory required per core in MB.
Defaults to 500MB per core as a safe estimate.

Returns:
int: Optimal number of cores to use (at least 1)
"""
# If specific cores requested (and not "auto"), respect that choice
if requested_cores not in (None, 'auto') and requested_cores > 0:
return min(requested_cores, cpu_count())

# Calculate based on available memory
free_memory_mb = psutil.virtual_memory().available / (1024 * 1024)
cores_by_memory = max(1, int(free_memory_mb / mb_per_core))

# Return the smaller of: cores based on memory or total available cores
return min(cores_by_memory, cpu_count())
Loading
Loading