AOT-biomaps 2.9.138__py3-none-any.whl → 2.9.279__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.

Potentially problematic release.


This version of AOT-biomaps might be problematic. Click here for more details.

Files changed (31) hide show
  1. AOT_biomaps/AOT_Acoustic/AcousticTools.py +35 -115
  2. AOT_biomaps/AOT_Acoustic/StructuredWave.py +2 -2
  3. AOT_biomaps/AOT_Acoustic/_mainAcoustic.py +22 -18
  4. AOT_biomaps/AOT_Experiment/Tomography.py +74 -4
  5. AOT_biomaps/AOT_Experiment/_mainExperiment.py +102 -68
  6. AOT_biomaps/AOT_Optic/_mainOptic.py +124 -58
  7. AOT_biomaps/AOT_Recon/AOT_Optimizers/DEPIERRO.py +72 -108
  8. AOT_biomaps/AOT_Recon/AOT_Optimizers/LS.py +474 -289
  9. AOT_biomaps/AOT_Recon/AOT_Optimizers/MAPEM.py +173 -68
  10. AOT_biomaps/AOT_Recon/AOT_Optimizers/MLEM.py +360 -154
  11. AOT_biomaps/AOT_Recon/AOT_Optimizers/PDHG.py +150 -111
  12. AOT_biomaps/AOT_Recon/AOT_PotentialFunctions/RelativeDifferences.py +10 -14
  13. AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/SparseSMatrix_CSR.py +281 -0
  14. AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/SparseSMatrix_SELL.py +328 -0
  15. AOT_biomaps/AOT_Recon/AOT_SparseSMatrix/__init__.py +2 -0
  16. AOT_biomaps/AOT_Recon/AOT_biomaps_kernels.cubin +0 -0
  17. AOT_biomaps/AOT_Recon/AlgebraicRecon.py +359 -238
  18. AOT_biomaps/AOT_Recon/AnalyticRecon.py +29 -41
  19. AOT_biomaps/AOT_Recon/BayesianRecon.py +165 -91
  20. AOT_biomaps/AOT_Recon/DeepLearningRecon.py +4 -1
  21. AOT_biomaps/AOT_Recon/PrimalDualRecon.py +175 -31
  22. AOT_biomaps/AOT_Recon/ReconEnums.py +38 -3
  23. AOT_biomaps/AOT_Recon/ReconTools.py +184 -77
  24. AOT_biomaps/AOT_Recon/__init__.py +1 -0
  25. AOT_biomaps/AOT_Recon/_mainRecon.py +144 -74
  26. AOT_biomaps/__init__.py +4 -36
  27. {aot_biomaps-2.9.138.dist-info → aot_biomaps-2.9.279.dist-info}/METADATA +2 -1
  28. aot_biomaps-2.9.279.dist-info/RECORD +47 -0
  29. aot_biomaps-2.9.138.dist-info/RECORD +0 -43
  30. {aot_biomaps-2.9.138.dist-info → aot_biomaps-2.9.279.dist-info}/WHEEL +0 -0
  31. {aot_biomaps-2.9.138.dist-info → aot_biomaps-2.9.279.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  from AOT_biomaps.Config import config
2
2
 
3
+ from scipy.signal import hilbert
3
4
  import os
4
5
  import h5py
5
6
  import numpy as np
@@ -109,136 +110,55 @@ def reshape_field(field, factor, device=None):
109
110
  if isinstance(field, np.ndarray):
110
111
  return downsampled.cpu().numpy()
111
112
  else:
112
- return downsampled
113
-
114
-
115
- def CPU_hilbert(signal, axis=0):
116
- """
117
- Compute the Hilbert transform of a real signal using NumPy.
118
-
119
- Parameters:
120
- - signal: Input real signal (numpy.ndarray).
121
- - axis: Axis along which to compute the Hilbert transform.
122
-
123
- Returns:
124
- - analytic_signal: The analytic signal of the input.
125
- """
126
- fft_signal = np.fft.fftn(signal, axes=[axis])
127
- h = np.zeros_like(signal)
128
-
129
- if axis == 0:
130
- h[0 : signal.shape[0] // 2 + 1, ...] = 1
131
- h[signal.shape[0] // 2 + 1 :, ...] = 2
132
- else:
133
- raise ValueError("Axis not supported for this implementation.")
134
-
135
- analytic_signal = np.fft.ifftn(fft_signal * h, axes=[axis])
136
- return analytic_signal
137
-
138
- def GPU_hilbert(signal, axis=0):
139
- """
140
- Compute the Hilbert transform of a real signal using PyTorch.
141
-
142
- Parameters:
143
- - signal: Input real signal (torch.Tensor).
144
- - axis: Axis along which to compute the Hilbert transform.
113
+ return downsampled.cpu().numpy()
145
114
 
146
- Returns:
147
- - analytic_signal: The analytic signal of the input.
115
+ def calculate_envelope_squared(field):
148
116
  """
149
- fft_signal = torch.fft.fftn(signal, dim=axis)
150
- h = torch.zeros_like(signal)
151
- if axis == 0:
152
- h[0 : signal.shape[0] // 2 + 1, ...] = 1
153
- h[signal.shape[0] // 2 + 1 :, ...] = 2
154
- else:
155
- raise ValueError("Axis not supported for this implementation.")
117
+ Calcule l'enveloppe au carré du champ acoustique en utilisant scipy.signal.hilbert (CPU uniquement).
156
118
 
157
- analytic_signal = torch.fft.ifftn(fft_signal * h, dim=axis)
158
- return analytic_signal
119
+ Args:
120
+ field: Champ acoustique (numpy.ndarray) de forme (T, X, Z) ou (T, X, Y, Z).
159
121
 
160
- def calculate_envelope_squared(field, isGPU):
161
- """
162
- Calculate the analytic envelope of the acoustic field using either CPU or GPU with PyTorch.
163
- Parameters:
164
- - field: Input acoustic field (numpy.ndarray or torch.Tensor).
165
- - isGPU (bool): If True, use GPU for computation. Otherwise, use CPU.
166
122
  Returns:
167
- - envelope (numpy.ndarray): The squared analytic envelope of the acoustic field.
123
+ envelope (numpy.ndarray): Enveloppe au carré du champ acoustique.
168
124
  """
169
125
  try:
170
126
  if field is None:
171
- raise ValueError("Acoustic field is not generated. Please generate the field first.")
127
+ raise ValueError("Le champ acoustique n'est pas généré. Veuillez d'abord générer le champ.")
172
128
 
173
- # Convert input to tensor (handle both numpy arrays and tensors)
174
- if isinstance(field, np.ndarray):
175
- acoustic_field = torch.from_numpy(field).to(dtype=torch.float32)
176
- else:
177
- acoustic_field = field.detach().clone().to(dtype=torch.float32)
178
-
179
- # Handle GPU/CPU transfer
180
- if isGPU:
181
- if not torch.cuda.is_available():
182
- print("CUDA is not available, falling back to CPU.")
183
- isGPU = False
129
+ if not isinstance(field, np.ndarray):
130
+ if hasattr(field, 'cpu'):
131
+ field = field.cpu().numpy() # Si c'est un tenseur PyTorch sur GPU/CPU
184
132
  else:
185
- # Check GPU memory
186
- free_memory = torch.cuda.get_device_properties(0).total_memory - torch.cuda.memory_allocated(0)
187
- required_memory = acoustic_field.numel() * acoustic_field.element_size()
188
- if free_memory < required_memory:
189
- print(f"GPU memory insufficient ({required_memory / (1024 ** 2):.2f} MB required, {free_memory / (1024 ** 2):.2f} MB free), falling back to CPU.")
190
- isGPU = False
191
- else:
192
- acoustic_field = acoustic_field.cuda()
193
-
194
- if len(acoustic_field.shape) not in [3, 4]:
195
- raise ValueError("Input acoustic field must be a 3D or 4D array.")
196
-
197
- def process_slice(slice_index):
198
- """Calculate the envelope for a given slice of the acoustic field."""
199
- slice_data = acoustic_field[slice_index]
200
-
201
- if len(acoustic_field.shape) == 3:
202
- if isGPU:
203
- return torch.abs(GPU_hilbert(slice_data, axis=0))**2
204
- else:
205
- return torch.from_numpy(np.abs(CPU_hilbert(slice_data.cpu().numpy(), axis=0))**2).to(dtype=torch.float32)
206
-
207
- elif len(acoustic_field.shape) == 4:
208
- if isGPU:
209
- return torch.stack([
210
- torch.abs(GPU_hilbert(slice_data[:, y, z], axis=0))**2
211
- for y in range(slice_data.shape[1])
212
- for z in range(slice_data.shape[2])
213
- ]).reshape(slice_data.shape[1], slice_data.shape[2], -1).permute(2, 0, 1)
214
- else:
215
- envelope = torch.zeros_like(slice_data)
216
- for y in range(slice_data.shape[1]):
217
- for z in range(slice_data.shape[2]):
218
- envelope[:, y, z] = torch.from_numpy(
219
- np.abs(CPU_hilbert(slice_data[:, y, z].cpu().numpy(), axis=0))**2
220
- )
221
- return envelope
222
-
223
- # Process slices
224
- num_slices = acoustic_field.shape[0]
225
- slice_indices = range(num_slices)
226
-
227
- if isGPU:
228
- envelopes = [process_slice(i) for i in slice_indices]
229
- else:
230
- with ThreadPoolExecutor() as executor:
231
- envelopes = list(executor.map(process_slice, slice_indices))
232
-
233
- # Combine results
234
- envelope = torch.stack(envelopes, axis=0)
235
- return envelope.cpu().numpy() if isGPU else envelope.numpy()
133
+ field = np.array(field) # Conversion générique
134
+
135
+ if len(field.shape) not in [3, 4]:
136
+ raise ValueError("Le champ acoustique doit être un tableau 3D (T, X, Z) ou 4D (T, X, Y, Z).")
137
+
138
+ # Calcul de l'enveloppe avec scipy.signal.hilbert
139
+ if len(field.shape) == 3:
140
+ T, X, Z = field.shape
141
+ envelope = np.zeros_like(field)
142
+ for x in range(X):
143
+ for z in range(Z):
144
+ envelope[:, x, z] = np.abs(hilbert(field[:, x, z], axis=0)) ** 2
145
+ elif len(field.shape) == 4:
146
+ T, X, Y, Z = field.shape
147
+ envelope = np.zeros_like(field)
148
+ for x in range(X):
149
+ for y in range(Y):
150
+ for z in range(Z):
151
+ envelope[:, x, y, z] = np.abs(hilbert(field[:, x, y, z], axis=0)) ** 2
152
+
153
+
154
+ return envelope
236
155
 
237
156
  except Exception as e:
238
- print(f"Error in calculate_envelope_squared method: {e}")
157
+ print(f"Erreur dans calculate_envelope_squared: {e}")
239
158
  raise
240
159
 
241
160
 
161
+
242
162
  def getPattern(pathFile):
243
163
  """
244
164
  Get the pattern from a file path.
@@ -292,8 +292,8 @@ class StructuredWave(AcousticField):
292
292
  f"!number format := short float\n"
293
293
  f"!number of bytes per pixel := 4\n"
294
294
  f"scaling factor (mm/pixel) [1] := {self.params['dx'] * 1000}\n"
295
- f"scaling factor (mm/pixel) [2] := {self.params['dx'] * 1000}\n"
296
- f"scaling factor (s/pixel) [3] := {1 / self.params['f_AQ']}\n"
295
+ f"scaling factor (mm/pixel) [2] := {self.params['dz'] * 1000}\n"
296
+ f"scaling factor (s/pixel) [3] := {1 / self.params['f_saving']}\n"
297
297
  f"first pixel offset (mm) [1] := {self.params['Xrange'][0] * 1e3}\n"
298
298
  f"first pixel offset (mm) [2] := {self.params['Zrange'][0] * 1e3}\n"
299
299
  f"first pixel offset (s) [3] := 0\n"
@@ -1,6 +1,6 @@
1
1
  import AOT_biomaps.Settings
2
2
  from AOT_biomaps.Config import config
3
- from AOT_biomaps.AOT_Acoustic.AcousticTools import calculate_envelope_squared, CPU_hilbert, loadmat
3
+ from AOT_biomaps.AOT_Acoustic.AcousticTools import calculate_envelope_squared, loadmat
4
4
  from .AcousticTools import next_power_of_2, reshape_field
5
5
  from .AcousticEnums import TypeSim, Dim, FormatSave, WaveType
6
6
 
@@ -19,10 +19,12 @@ from kwave.kspaceFirstOrder3D import kspaceFirstOrder3D
19
19
  from kwave.kspaceFirstOrder2D import kspaceFirstOrder2D
20
20
  from kwave.options.simulation_options import SimulationOptions
21
21
  from kwave.options.simulation_execution_options import SimulationExecutionOptions
22
+ from AOT_biomaps.Settings import Params
22
23
 
23
24
  from tempfile import gettempdir
24
25
  from math import ceil
25
26
  from abc import ABC, abstractmethod
27
+ import logging
26
28
 
27
29
 
28
30
 
@@ -75,7 +77,7 @@ class AcousticField(ABC):
75
77
  print(f"Initialization error: {e}")
76
78
  raise
77
79
  if params != None:
78
- if type(params) != AOT_biomaps.Settings.Params:
80
+ if type(params) != Params:
79
81
  raise TypeError("params must be an instance of the Params class")
80
82
 
81
83
  self.params = {
@@ -225,6 +227,7 @@ class AcousticField(ABC):
225
227
  Generate the acoustic field based on the specified simulation type and parameters.
226
228
  """
227
229
  try:
230
+ logging.getLogger('root').setLevel(logging.ERROR)
228
231
  if self.params['typeSim'] == TypeSim.FIELD2.value:
229
232
  raise NotImplementedError("FIELD2 simulation is not implemented yet.")
230
233
  elif self.params['typeSim'] == TypeSim.KWAVE.value:
@@ -233,10 +236,10 @@ class AcousticField(ABC):
233
236
  field = self._generate_acoustic_field_KWAVE_2D(isGpu, show_log)
234
237
  except Exception as e:
235
238
  raise RuntimeError(f"Failed to generate 2D acoustic field: {e}")
236
- self.field = calculate_envelope_squared(field, isGpu)
239
+ self.field = reshape_field(calculate_envelope_squared(field),[self.factorT, self.factorX, self.factorZ])
237
240
  elif self.params["dim"] == Dim.D3.value:
238
241
  field = self._generate_acoustic_field_KWAVE_3D(isGpu, show_log)
239
- self.field = calculate_envelope_squared(field, isGpu)
242
+ self.field = reshape_field(calculate_envelope_squared(field),[self.factorT, self.factorX, self.factorZ])
240
243
  elif self.params['typeSim'] == TypeSim.HYDRO.value:
241
244
  raise ValueError("Cannot generate field for Hydrophone simulation, load exciting acquisitions.")
242
245
  else:
@@ -265,7 +268,7 @@ class AcousticField(ABC):
265
268
  print(f"Error in save_field method: {e}")
266
269
  raise
267
270
 
268
- def load_field(self, folderPath, formatSave=FormatSave.HDR_IMG):
271
+ def load_field(self, folderPath, formatSave=FormatSave.HDR_IMG, nameBlock=None):
269
272
  """
270
273
  Load the acoustic field from a file in the specified format.
271
274
 
@@ -286,7 +289,7 @@ class AcousticField(ABC):
286
289
  raise NotImplementedError("3D KWAVE field loading is not implemented yet.")
287
290
  elif formatSave.value == FormatSave.H5.value:
288
291
  if self.params["dim"] == Dim.D2.value:
289
- self._load_field_h5(folderPath)
292
+ self._load_field_h5(folderPath,nameBlock)
290
293
  elif self.params["dim"] == Dim.D3.value:
291
294
  raise NotImplementedError("H5 KWAVE field loading is not implemented yet.")
292
295
  elif formatSave.value == FormatSave.NPY.value:
@@ -502,7 +505,7 @@ class AcousticField(ABC):
502
505
  try:
503
506
  # --- 1. Grid setup ---
504
507
  dx = self.params['dx']
505
- if dx >= self.params['element_width']:
508
+ if dx >= self.params['element_width']*2:
506
509
  dx = self.params['element_width'] / 2
507
510
  Nx = int(round((self.params['Xrange'][1] - self.params['Xrange'][0]) / dx))
508
511
  Nz = int(round((self.params['Zrange'][1] - self.params['Zrange'][0]) / dx))
@@ -511,9 +514,9 @@ class AcousticField(ABC):
511
514
  Nz = self.params['Nz']
512
515
 
513
516
  # --- 2. Time and space factors ---
514
- factorT = int(np.ceil(self.params['f_AQ'] / self.params['f_saving']))
515
- factorX = int(np.ceil(Nx / self.params['Nx']))
516
- factorZ = int(np.ceil(Nz / self.params['Nz']))
517
+ self.factorT = int(np.ceil(self.params['f_AQ'] / self.params['f_saving']))
518
+ self.factorX = int(np.ceil(Nx / self.params['Nx']))
519
+ self.factorZ = int(np.ceil(Nz / self.params['Nz']))
517
520
 
518
521
  # --- 3. Grid and source initialization ---
519
522
  kgrid = kWaveGrid([Nx, Nz], [dx, dx])
@@ -531,6 +534,8 @@ class AcousticField(ABC):
531
534
  total_size_z = next_power_of_2(Nz)
532
535
  pml_x_size = (total_size_x - Nx) // 2
533
536
  pml_z_size = (total_size_z - Nz) // 2
537
+ pml_x_size = max(pml_x_size, 50) # Ensure a minimum PML size of 50 grid points to avoid parasitic reflections
538
+ pml_z_size = max(pml_z_size, 50) # Ensure a minimum PML size of 50 grid points to avoid parasitic reflections
534
539
 
535
540
  # --- 6. Simulation options ---
536
541
  simulation_options = SimulationOptions(
@@ -549,7 +554,7 @@ class AcousticField(ABC):
549
554
  )
550
555
 
551
556
  # --- 7. Call specialized function to set up source.p_mask and source.p ---
552
- self._SetUpSource(source, Nx, dx, factorT)
557
+ self._SetUpSource(source, Nx, dx, self.factorT)
553
558
 
554
559
  # --- 8. Run simulation ---
555
560
  sensor_data = kspaceFirstOrder2D(
@@ -563,10 +568,7 @@ class AcousticField(ABC):
563
568
 
564
569
  # --- 9. Post-process results ---
565
570
  data = sensor_data['p'].reshape(kgrid.Nt, Nz, Nx)
566
- if factorT != 1 or factorX != 1 or factorZ != 1:
567
- return reshape_field(data, [factorT, factorX, factorZ])
568
- else:
569
- return data
571
+ return data
570
572
 
571
573
  except Exception as e:
572
574
  print(f"Error generating 2D acoustic field: {e}")
@@ -653,7 +655,7 @@ class AcousticField(ABC):
653
655
  """
654
656
  pass
655
657
 
656
- def _load_field_h5(self, filePath):
658
+ def _load_field_h5(self, filePath,nameBlock):
657
659
  """
658
660
  Load the 2D acoustic field from an H5 file.
659
661
 
@@ -664,8 +666,10 @@ class AcousticField(ABC):
664
666
  - field (numpy.ndarray): The loaded acoustic field.
665
667
  """
666
668
  try:
667
- with h5py.File(filePath+self.getName_field()+".h5", 'r') as f:
668
- self.field = f['data'][:]
669
+ if nameBlock is None:
670
+ nameBlock = 'data'
671
+ with h5py.File(os.path.join(filePath, self.getName_field()+".h5"), 'r') as f:
672
+ self.field = f[nameBlock][:]
669
673
  except Exception as e:
670
674
  print(f"Error in _load_field_h5 method: {e}")
671
675
  raise
@@ -7,6 +7,8 @@ import psutil
7
7
  import numpy as np
8
8
  import matplotlib.pyplot as plt
9
9
  from tqdm import trange
10
+ import h5py
11
+ from scipy.io import loadmat
10
12
 
11
13
  class Tomography(Experiment):
12
14
  def __init__(self, **kwargs):
@@ -47,7 +49,7 @@ class Tomography(Experiment):
47
49
  return False, f"OpticImage phantom shape {self.OpticImage.phantom.shape} does not match AcousticFields shape {self.AcousticFields[0].field.shape[1:]}."
48
50
  return True, "Experiment is correctly initialized."
49
51
 
50
- def generateAcousticFields(self, fieldDataPath=None, show_log=True):
52
+ def generateAcousticFields(self, fieldDataPath=None, show_log=True, nameBlock=None):
51
53
  """
52
54
  Generate the acoustic fields for simulation.
53
55
  Args:
@@ -57,7 +59,7 @@ class Tomography(Experiment):
57
59
  systemMatrix: A numpy array of the generated fields.
58
60
  """
59
61
  if self.TypeAcoustic.value == WaveType.StructuredWave.value:
60
- self.AcousticFields = self._generateAcousticFields_STRUCT_CPU(fieldDataPath, show_log)
62
+ self.AcousticFields = self._generateAcousticFields_STRUCT_CPU(fieldDataPath, show_log, nameBlock)
61
63
  else:
62
64
  raise ValueError("Unsupported wave type.")
63
65
 
@@ -389,7 +391,7 @@ class Tomography(Experiment):
389
391
  return True
390
392
 
391
393
  # PRIVATE METHODS
392
- def _generateAcousticFields_STRUCT_CPU(self, fieldDataPath=None, show_log=False):
394
+ def _generateAcousticFields_STRUCT_CPU(self, fieldDataPath=None, show_log=False, nameBlock=None):
393
395
  if self.patterns is None:
394
396
  raise ValueError("patterns is not initialized. Please load or generate the active list first.")
395
397
  listAcousticFields = []
@@ -415,7 +417,7 @@ class Tomography(Experiment):
415
417
  if pathField is not None and os.path.exists(pathField):
416
418
  progress_bar.set_postfix_str(f"Loading field - {AcousticField.getName_field()} -- Memory used: {memory.percent}%")
417
419
  try:
418
- AcousticField.load_field(fieldDataPath, self.FormatSave)
420
+ AcousticField.load_field(fieldDataPath, self.FormatSave,nameBlock)
419
421
  except:
420
422
  progress_bar.set_postfix_str(f"Error loading field -> Generating field - {AcousticField.getName_field()} -- Memory used: {memory.percent}% ---- processing on {config.get_process().upper()} ----")
421
423
  AcousticField.generate_field(show_log=show_log)
@@ -433,3 +435,71 @@ class Tomography(Experiment):
433
435
  listAcousticFields.append(AcousticField)
434
436
  progress_bar.set_postfix_str("")
435
437
  return listAcousticFields
438
+
439
+ def load_experimentalAO(self, pathAO, withTumor = True, h5name='AOsignal'):
440
+ """
441
+ Load experimental AO signals from specified file paths.
442
+ Args:
443
+ path_withTumor: Path to the AO signal with tumor.
444
+ path_withoutTumor: Path to the AO signal without tumor.
445
+ """
446
+ if not os.path.exists(pathAO):
447
+ raise FileNotFoundError(f"File {pathAO} not found.")
448
+
449
+ if pathAO.endswith('.npy'):
450
+ ao_signal = np.load(pathAO)
451
+ elif pathAO.endswith('.h5'):
452
+ with h5py.File(pathAO, 'r') as f:
453
+ if h5name not in f:
454
+ raise KeyError(f"Dataset '{h5name}' not found in the HDF5 file.")
455
+ ao_signal = f[h5name][:]
456
+ elif pathAO.endswith('.mat'):
457
+ mat_data = loadmat(pathAO)
458
+ if h5name not in mat_data:
459
+ raise KeyError(f"Dataset '{h5name}' not found in the .mat file.")
460
+ ao_signal = mat_data[h5name]
461
+ elif pathAO.endswith('.hdr'):
462
+ ao_signal = self._loadAOSignal(pathAO)
463
+ else:
464
+ raise ValueError("Unsupported file format. Supported formats are: .npy, .h5, .mat, .hdr")
465
+
466
+ if withTumor:
467
+ self.AOsignal_withTumor = ao_signal
468
+ else:
469
+ self.AOsignal_withoutTumor = ao_signal
470
+
471
+ def check_experimentalAO(self, activeListPath, withTumor=True):
472
+ """
473
+ Check if the experimental AO signals are correctly initialized.
474
+ """
475
+ if withTumor:
476
+ if self.AOsignal_withTumor is None:
477
+ raise ValueError("Experimental AOsignal with tumor is not initialized. Please load the experimental AO signal with tumor first.")
478
+ else:
479
+ if self.AOsignal_withoutTumor is None:
480
+ raise ValueError("Experimental AOsignal without tumor is not initialized. Please load the experimental AO signal without tumor first.")
481
+ if self.AcousticFields is not None:
482
+ # get min time shape between all AO signals
483
+ print()
484
+
485
+ if self.AcousticFields[0].field.shape[0] > self.AOsignal_withTumor.shape[0]:
486
+ self.cutAcousticFields(max_t=self.AOsignal_withTumor.shape[0]/float(self.params.acoustic['f_saving']))
487
+ else:
488
+ for i in range(len(self.AcousticFields)):
489
+ min_time_shape = min(self.AcousticFields[i].field.shape[0])
490
+ if withTumor:
491
+ self.AOsignal_withTumor = self.AOsignal_withTumor[:min_time_shape, :]
492
+ else:
493
+ self.AOsignal_withoutTumor = self.AOsignal_withoutTumor[:min_time_shape, :]
494
+
495
+ for field in self.AcousticFields:
496
+ if activeListPath is not None:
497
+ with open(activeListPath, 'r') as file:
498
+ lines = file.readlines()
499
+ expected_name = lines[self.AcousticFields.index(field)].strip()
500
+ nameField = field.getName_field()
501
+ if nameField.startswith("field_"):
502
+ nameField = nameField[len("field_"):]
503
+ if nameField != expected_name:
504
+ raise ValueError(f"Field name {nameField} does not match the expected name {expected_name} from the active list.")
505
+ print("Experimental AO signals are correctly initialized.")