Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gn mcx based on t265 #391

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 24 additions & 0 deletions docs/source/simpa.core.simulation_modules.acoustic_module.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
acoustic\_module
=======================================================

.. automodule:: simpa.core.simulation_modules.acoustic_module
:members:
:undoc-members:
:show-inheritance:

.. automodule:: simpa.core.simulation_modules.acoustic_module.acoustic_adapter_base
:members:
:undoc-members:
:show-inheritance:


.. automodule:: simpa.core.simulation_modules.acoustic_module.acoustic_test_adapter
:members:
:undoc-members:
:show-inheritance:


.. automodule:: simpa.core.simulation_modules.acoustic_module.k_wave_adapter
:members:
:undoc-members:
:show-inheritance:
36 changes: 36 additions & 0 deletions docs/source/simpa.core.simulation_modules.optical_module.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
optical\_module
======================================================

.. automodule:: simpa.core.simulation_modules.optical_module
:members:
:undoc-members:
:show-inheritance:

.. automodule:: simpa.core.simulation_modules.optical_module.mcx_adapter
:members:
:undoc-members:
:show-inheritance:


.. automodule:: simpa.core.simulation_modules.optical_module.mcx_reflectance_adapter
:members:
:undoc-members:
:show-inheritance:


.. automodule:: simpa.core.simulation_modules.optical_module.optical_adapter_base
:members:
:undoc-members:
:show-inheritance:


.. automodule:: simpa.core.simulation_modules.optical_module.optical_test_adapter
:members:
:undoc-members:
:show-inheritance:


.. automodule:: simpa.core.simulation_modules.optical_module.volume_boundary_condition
:members:
:undoc-members:
:show-inheritance:
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ optical\_simulation\_module
:members:
:undoc-members:
:show-inheritance:


.. automodule:: simpa.core.simulation_modules.optical_simulation_module.volume_boundary_condition
:members:
:undoc-members:
:show-inheritance:
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
refractive\_index\_spectra\_data
==============================================================

.. automodule:: simpa.utils.libraries.refractive_index_spectra_data
:members:
:undoc-members:
:show-inheritance:
1 change: 1 addition & 0 deletions docs/source/simpa.utils.libraries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ libraries

simpa.utils.libraries.absorption_spectra_data
simpa.utils.libraries.anisotropy_spectra_data
simpa.utils.libraries.refractive_index_spectra_data
simpa.utils.libraries.scattering_spectra_data
simpa.utils.libraries.structure_library

Expand Down
7 changes: 7 additions & 0 deletions docs/source/three_vs_two_dimensional_simulation_example.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
three_vs_two_dimensional_simulation_example
=========================================

.. literalinclude:: ../../simpa_examples/three_vs_two_dimensional_simulation_example.py
:language: python
:lines: 1-

Empty file added poetry.lock
Empty file.
7 changes: 7 additions & 0 deletions pre_commit_configs/link-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"ignorePatterns": [
{
"pattern": "https://doi.org/10.1117/1.JBO.27.8.083010"
}
]
}
64 changes: 18 additions & 46 deletions simpa/core/simulation_modules/optical_module/mcx_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def forward_model(self,
absorption_cm: np.ndarray,
scattering_cm: np.ndarray,
anisotropy: np.ndarray,
refractive_index: np.ndarray,
illumination_geometry: IlluminationGeometryBase) -> Dict:
"""
runs the MCX simulations. Binary file containing scattering and absorption volumes is temporarily created as
Expand All @@ -52,22 +53,17 @@ def forward_model(self,
:param absorption_cm: array containing the absorption of the tissue in `cm` units
:param scattering_cm: array containing the scattering of the tissue in `cm` units
:param anisotropy: array containing the anisotropy of the volume defined by `absorption_cm` and `scattering_cm`
:param refractive_index: array containing the refractive index of the volume defined by `absorption_cm` and `scattering_cm`
:param illumination_geometry: and instance of `IlluminationGeometryBase` defining the illumination geometry
:return: `Dict` containing the results of optical simulations, the keys in this dictionary-like object
depend on the Tags defined in `self.component_settings`
"""
if Tags.MCX_ASSUMED_ANISOTROPY in self.component_settings:
_assumed_anisotropy = self.component_settings[Tags.MCX_ASSUMED_ANISOTROPY]
else:
_assumed_anisotropy = 0.9

self.generate_mcx_bin_input(absorption_cm=absorption_cm,
scattering_cm=scattering_cm,
anisotropy=anisotropy,
assumed_anisotropy=_assumed_anisotropy)
refractive_index=refractive_index)

settings_dict = self.get_mcx_settings(illumination_geometry=illumination_geometry,
assumed_anisotropy=_assumed_anisotropy)
settings_dict = self.get_mcx_settings(illumination_geometry=illumination_geometry)

print(settings_dict)
self.generate_mcx_json_input(settings_dict=settings_dict)
Expand Down Expand Up @@ -99,14 +95,12 @@ def generate_mcx_json_input(self, settings_dict: Dict) -> None:

def get_mcx_settings(self,
illumination_geometry: IlluminationGeometryBase,
assumed_anisotropy: np.ndarray,
**kwargs) -> Dict:
"""
generates MCX-specific settings for simulations based on Tags in `self.global_settings` and
`self.component_settings` . Among others, it defines the volume type, dimensions and path to binary file.

:param illumination_geometry: and instance of `IlluminationGeometryBase` defining the illumination geometry
:param assumed_anisotropy:
:param kwargs: dummy, used for class inheritance
:return: dictionary with settings to be used by MCX
"""
Expand Down Expand Up @@ -148,15 +142,9 @@ def get_mcx_settings(self,
"mus": 0,
"g": 1,
"n": 1
},
{
"mua": 1,
"mus": 1,
"g": assumed_anisotropy,
"n": 1
}
],
"MediaFormat": "muamus_float",
"MediaFormat": "asgn_float",
"Dim": [self.nx, self.ny, self.nz],
"VolumeFile": self.global_settings[Tags.SIMULATION_PATH] + "/" +
self.global_settings[Tags.VOLUME_NAME] + ".bin"
Expand Down Expand Up @@ -207,22 +195,22 @@ def generate_mcx_bin_input(self,
absorption_cm: np.ndarray,
scattering_cm: np.ndarray,
anisotropy: np.ndarray,
assumed_anisotropy: np.ndarray) -> None:
refractive_index: np.ndarray) -> None:
"""
generates binary file containing volume scattering and absorption as input for MCX

:param absorption_cm: Absorption in units of per centimeter
:param scattering_cm: Scattering in units of per centimeter
:param anisotropy: Dimensionless scattering anisotropy
:param assumed_anisotropy:
:param refractive_index: Refractive index
:return: None
"""
absorption_mm, scattering_mm = self.pre_process_volumes(**{'absorption_cm': absorption_cm,
'scattering_cm': scattering_cm,
'anisotropy': anisotropy,
'assumed_anisotropy': assumed_anisotropy})
# stack arrays to give array with shape (nx,ny,nz,2)
op_array = np.stack([absorption_mm, scattering_mm], axis=-1, dtype=np.float32)
absorption_mm, scattering_mm, anisotropy, refractive_index = self.pre_process_volumes(**{'absorption_cm': absorption_cm,
'scattering_cm': scattering_cm,
'anisotropy': anisotropy,
'refractive_index': refractive_index})
# stack arrays to give array with shape (nx,ny,nz,4) - where the 4 floats correspond to mua/mus/g/n
op_array = np.stack([absorption_mm, scattering_mm, anisotropy, refractive_index], axis=-1, dtype=np.float32)
[self.nx, self.ny, self.nz, _] = np.shape(op_array)
# # create a binary of the volume
tmp_input_path = self.global_settings[Tags.SIMULATION_PATH] + "/" + \
Expand All @@ -240,11 +228,9 @@ def read_mcx_output(self, **kwargs) -> Dict:
"""
content = jdata.load(self.mcx_volumetric_data_file)
fluence = content['NIFTIData']
print(f"fluence.shape {fluence.shape}")
if fluence.ndim > 3:
# remove the 1 or 2 (for mcx >= v2024.1) additional dimensions of size 1 if present to obtain a 3d array
fluence = fluence.reshape(fluence.shape[0], fluence.shape[1], -1)
print(f"fluence.shape {fluence.shape}")
results = dict()
results[Tags.DATA_FIELD_FLUENCE] = fluence
return results
Expand All @@ -263,8 +249,7 @@ def pre_process_volumes(self, **kwargs) -> Tuple:
"""
pre-process volumes before running simulations with MCX. The volumes are transformed to `mm` units

:param kwargs: dictionary containing at least the keys `scattering_cm, absorption_cm, anisotropy` and
`assumed_anisotropy`
:param kwargs: dictionary containing at least the keys `scattering_cm, absorption_cm, anisotropy, refractive_index`
:return: `Tuple` of volumes after transformation
"""
return self.volumes_to_mm(**kwargs)
Expand All @@ -274,29 +259,16 @@ def volumes_to_mm(**kwargs) -> Tuple:
"""
transforms volumes into `mm` units

:param kwargs: dictionary containing at least the keys `scattering_cm, absorption_cm, anisotropy` and
`assumed_anisotropy`
:param kwargs: dictionary containing at least the keys `scattering_cm, absorption_cm, anisotropy, refractive_index`
:return: `Tuple` of volumes after transformation
"""
scattering_cm = kwargs.get('scattering_cm')
absorption_cm = kwargs.get('absorption_cm')
anisotropy = kwargs.get('anisotropy')
refractive_index = kwargs.get('refractive_index')
absorption_mm = absorption_cm / 10
scattering_mm = scattering_cm / 10

# FIXME Currently, mcx only accepts a single value for the anisotropy.
# In order to use the correct reduced scattering coefficient throughout the simulation,
# we adjust the scattering parameter to be more accurate in the diffuse regime.
# This will lead to errors, especially in the quasi-ballistic regime.

given_reduced_scattering = (scattering_mm * (1 - kwargs.get('anisotropy')))

# If the anisotropy is 1, all scattering is forward scattering which is equal to no scattering at all
if kwargs.get("assumed_anisotropy") == 1:
scattering_mm = given_reduced_scattering * 0
else:
scattering_mm = given_reduced_scattering / (1 - kwargs.get('assumed_anisotropy'))
scattering_mm[scattering_mm < 1e-10] = 1e-10
return absorption_mm, scattering_mm
return absorption_mm, scattering_mm, anisotropy, refractive_index

@staticmethod
def post_process_volumes(**kwargs) -> Tuple:
Expand Down
Loading
Loading