Coverage for kwave/kmedium.py: 51%
67 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-24 11:55 -0700
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-24 11:55 -0700
1from dataclasses import dataclass
2from typing import List
3from warnings import warn
5import numpy as np
6import kwave.utils.misc as util
7import kwave.utils.checkutils
10@dataclass
11class kWaveMedium(object):
12 sound_speed : np.array #: sound speed distribution within the acoustic medium [m/s] | required to be defined
13 sound_speed_ref : np.array = None #: reference sound speed used within the k-space operator (phase correction term) [m/s]
14 density : np.array = None #: density distribution within the acoustic medium [kg/m^3]
15 alpha_coeff : np.array = None #: power law absorption coefficient [dB/(MHz^y cm)]
16 alpha_power : np.array = None #: power law absorption exponent
17 alpha_mode : np.array = None #: optional input to force either the absorption or dispersion terms in the equation of state to be excluded; valid inputs are 'no_absorption' or 'no_dispersion'
18 alpha_filter : np.array = None #: frequency domain filter applied to the absorption and dispersion terms in the equation of state
19 alpha_sign : np.array = None #: two element array used to control the sign of absorption and dispersion terms in the equation of state
20 BonA : np.array = None #: parameter of nonlinearity
21 absorbing : bool = False #: is the medium absorbing?
22 stokes : bool = False #: is the medium absorbing stokes?
24 # """
25 # Note: For heterogeneous medium parameters, medium.sound_speed and
26 # medium.density must be given in matrix form with the same dimensions as
27 # kgrid. For homogeneous medium parameters, these can be given as single
28 # numeric values. If the medium is homogeneous and velocity inputs or
29 # outputs are not required, it is not necessary to specify medium.density.
30 # """
32 def __post_init__(self):
33 self.sound_speed = np.atleast_1d(self.sound_speed)
35 def check_fields(self, kgrid_shape: np.ndarray) -> None:
36 """
37 Check whether the given properties are valid
39 Args:
40 kgrid_shape: Shape of the kWaveGrid
42 Returns:
43 None
44 """
45 # check the absorption mode input is valid
46 if self.alpha_mode is not None:
47 assert self.alpha_mode in ['no_absorption', 'no_dispersion', 'stokes'], \
48 "medium.alpha_mode must be set to 'no_absorption', 'no_dispersion', or 'stokes'."
50 # check the absorption filter input is valid
51 if self.alpha_filter is not None and not (self.alpha_filter.shape == kgrid_shape).all():
52 raise ValueError('medium.alpha_filter must be the same size as the computational grid.')
54 # check the absorption sign input is valid
55 if self.alpha_sign is not None and (not kwave.utils.checkutils.is_number(self.alpha_sign) or (self.alpha_sign.size != 2)):
56 raise ValueError('medium.alpha_sign must be given as a 2 element numerical array controlling absorption and dispersion, respectively.')
58 # check alpha_coeff is non-negative and real
59 if not np.all(np.isreal(self.alpha_coeff)) or np.any(self.alpha_coeff < 0):
60 raise ValueError('medium.alpha_coeff must be non-negative and real.')
62 def is_defined(self, *fields) -> List[bool]:
63 """
64 Check if the field(s) are defined or None
66 Args:
67 *fields: String list of the fields
69 Returns:
70 Boolean list
71 """
72 results = []
73 for f in fields:
74 results.append(getattr(self, f) is not None)
75 return results
77 def ensure_defined(self, *fields) -> None:
78 """
79 Assert that the field(s) are defined (not None)
81 Args:
82 *fields: String list of the fields
84 Returns:
85 None
86 """
87 for f in fields:
88 assert getattr(self, f) is not None, f'The field {f} must be not be None'
90 def is_nonlinear(self) -> bool:
91 """
92 Check if the medium is nonlinear
94 Returns:
95 whether the fluid simulation is nonlinear
96 """
97 return self.BonA is not None
99 def set_absorbing(self, is_absorbing, is_stokes=False) -> None:
100 """
101 Change medium's absorbing and stokes properties
103 Args:
104 is_absorbing: Is the medium absorbing
105 is_stokes: Is the medium stokes
106 Returns:
107 None
108 """
109 # only stokes absorption is supported in the axisymmetric code
110 self.absorbing, self.stokes = is_absorbing, is_stokes
111 if is_absorbing:
112 if is_stokes:
113 self._check_absorbing_with_stokes()
114 else:
115 self._check_absorbing_without_stokes()
117 def _check_absorbing_without_stokes(self) -> None:
118 """
119 Check if the medium properties are set correctly for absorbing simulation without stokes
121 Returns:
122 None
123 """
124 # enforce both absorption parameters
125 self.ensure_defined('alpha_coeff', 'alpha_power')
127 # check y is a scalar
128 assert np.isscalar(self.alpha_power), 'medium.alpha_power must be scalar.'
130 # check y is real and within 0 to 3
131 assert np.all(np.isreal(self.alpha_coeff)) and 0 < self.alpha_power < 3, \
132 'medium.alpha_power must be a real number between 0 and 3.'
134 # display warning if y is close to 1 and the dispersion term has not been set to zero
135 if self.alpha_mode != 'no_dispersion':
136 assert self.alpha_power != 1, \
137 """The power law dispersion term in the equation of state is not valid for medium.alpha_power = 1.
138 This error can be avoided by choosing a power law exponent close to, but not exactly, 1.
139 If modelling acoustic absorption for medium.alpha_power = 1 is important and modelling dispersion is not
140 critical, this error can also be avoided by setting medium.alpha_mode to 'no_dispersion'"""
142 def _check_absorbing_with_stokes(self):
143 """
144 Check if the medium properties are set correctly for absorbing simulation with stokes
146 Returns:
147 None
148 """
149 # enforce absorption coefficient
150 self.ensure_defined('alpha_coeff')
152 # give warning if y is specified
153 if self.alpha_power is not None and (self.alpha_power.size != 1 or self.alpha_power != 2):
154 warn('WARNING: the axisymmetric code and stokes absorption assume alpha_power = 2, user value ignored.')
156 # overwrite y value
157 self.alpha_power = 2
159 # don't allow medium.alpha_mode with the axisymmetric code
160 if self.alpha_mode is not None and (self.alpha_mode in ['no_absorption', 'no_dispersion']):
161 raise NotImplementedError('Input option medium.alpha_mode is not supported with the axisymmetric code '
162 'or medium.alpha_mode = ''stokes''.')
164 # don't allow alpha_filter with stokes absorption (no variables are applied in k-space)
165 assert self.alpha_filter is None, \
166 "Input option medium.alpha_filter is not supported with the axisymmetric code " \
167 "or medium.alpha_mode = 'stokes'. "
169 ##########################################
170 # Elastic-code related properties - raise error when accessed
171 ##########################################
172 _ELASTIC_CODE_ACCESS_ERROR_TEXT_ = 'Elastic simulation and related properties are not supported!'
174 @property
175 def sound_speed_shear(self): # pragma: no cover
176 """
177 Shear sound speed (used in elastic simulations | not supported currently!)
178 """
179 raise NotImplementedError(self._ELASTIC_CODE_ACCESS_ERROR_TEXT_)
181 @property
182 def sound_speed_ref_shear(self): # pragma: no cover
183 """
184 Shear sound speed reference (used in elastic simulations | not supported currently!)
185 """
186 raise NotImplementedError(self._ELASTIC_CODE_ACCESS_ERROR_TEXT_)
188 @property
189 def sound_speed_compression(self): # pragma: no cover
190 """
191 Compression sound speed (used in elastic simulations | not supported currently!)
192 """
193 raise NotImplementedError(self._ELASTIC_CODE_ACCESS_ERROR_TEXT_)
195 @property
196 def sound_speed_ref_compression(self): # pragma: no cover
197 """
198 Compression sound speed reference (used in elastic simulations | not supported currently!)
199 """
200 raise NotImplementedError(self._ELASTIC_CODE_ACCESS_ERROR_TEXT_)
202 @property
203 def alpha_coeff_compression(self): # pragma: no cover
204 """
205 Compression alpha coefficient (used in elastic simulations | not supported currently!)
206 """
207 raise NotImplementedError(self._ELASTIC_CODE_ACCESS_ERROR_TEXT_)
209 @property
210 def alpha_coeff_shear(self): # pragma: no cover
211 """
212 Shear alpha coefficient (used in elastic simulations | not supported currently!)
213 """
214 raise NotImplementedError(self._ELASTIC_CODE_ACCESS_ERROR_TEXT_)