#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Particle properties (:mod:`hydrac.model.particle`)
==================================================
.. autoclass:: Particle
:members:
:private-members:
"""
from .model import Model
import numpy as np
from hydrac.util.query import query_yes_no
from hydrac.util.parameters import Parameters
class ParticleNatureError(Exception):
pass
[docs]class Particle(Model):
"""
Particle properties class.
Base class : :class:`hydrac.model.model.Model`
This class prepares the computation of the scattering models. Indeed, the
scattering properties of particles greatly differ given their shape, their
density compared to the one of the surrounding water or the sound speed in
the particles compared to the on in water (acoustic impedence contrasts).
.. image:: ../_image/part.pdf
:width: 80%
:align: center
Also, several methods exist to compute a model. Those can be empirical,
parametric, exact, approximated... All these techniques can be of interest
for diverse communities and are gathered here in hydrac.
The following class makes a distinction between organic and mineral like
particles, as being the source of major differences in terms of model
approximations and computational strategy used. Also, a distinction is made
between theory-based and empirism-based models for the same reason.
For the generation of a new scattering model, all this information needs to
be prompted by the user when asked.
The following models can be designed :
1 - Analytical modal series solution for elastic spheres
(Gaunaurd & Uberall 1983), straigt and bent
cylinders (Stanton et al. 1988).
2 - Distorted Wave Born Approximation (DWBA) models for weakly-scattering
bodies (fluid-like zooplankton), arbitrarily shaped with circular
cross-section (spheres, prolate/oblate spheroïds, straight cylinders,
uniformly bent cylinders, ...) (Stanton et al. 1998).
(N.B. In this case the chosen model name has to contain "DWBA")
3 - Heuristic hybrid models for marine and estuarine aggregates formed
under the process of flocculation (Thorne et al 2014)
4 - Heuristic and high-pass empirical models (Thorne & Hanes 2002,
Stanton et al. 1989)
All the parameters indicated by the user are saved in a modified
dictionnary ``param`` from the class
:class:`hydrac.util.parameters.AttrDict`, and are further accessed by the
class :class:`hydrac.model.scattering.scattering.Scattering` to compute the
model.
.. list-table:: Description of the ``param`` for particle properties
:widths: 10 25 25
:header-rows: 1
* - Parameter Name
- Type
- Description
* - :math:`c_p`
- float
- Compressional sound velocity in the particle
* - :math:`c_s`
- float
- Shear sound velocity in the particle
* - :math:`c`
- float
- Apparent velocity :math:`c=\\sqrt(c_p^2-\\frac{4}{3}c_s^2)`
* - mod_num
- int
- Number of modes to compute the analytical solution for
THQ models (set to 0 otherwise )
* - :math:`rhos`
- float
- Particle density
* - :math:`C_f`
- float
- Flocculation degree (only for HYB models)
* - :math:`m`
- float
- Fractal exponent (only for HYB models)
* - :math:`shape`
- :class:`hydrac.util.parameters.AttrDict`
- Animal shape-related parameters (for DWBA models)
* - :math:`phy`
- :class:`hydrac.util.parameters.AttrDict`
- Animal physical properties (for DWBA models)
* - :math:`simu`
- :class:`hydrac.util.parameters.AttrDict`
- DWBA simulation parameters (for DWBA models)
A typical example of a model parameters creation is as follows::
# We want to design a model names 'Model_1'
>>> P=Particle('Model_1')
# This model is not in the databese, so the user is prompted for the
# model type
Model type : THQ, EMP or HP?
THQ
# The user is prompted for the particle nature:
What nature : ORG, MIN or HYB ?
MIN
# The user is prompted fot the particle shape.
What shape : SPH or sCYL or bCYL ?
SPH
# The user is then prompted for the particles physical properties
Density (kg/m^3):
2650
Compressional velocity in the particle (m/s) :
5500
Shear velocity in the particle (m/s) :
3500
Number of modes to compute in the modal series soltion ?
20
# The user is then asked whether he wishes to save the current presets.
Do you want to save the custom model ? [Y/n]
Y
# In that case, the presets are saved in an xml file an the next time the
# user wants to use this model no more prompts will appear
>>>P2=hydrac.model.particle.Particle('Model_1')
"""
def __init__(self, name=None, default=None, rhos=None, c=None, cs=None,
cp=None, mtype=None, nature=None, shape=None, mod_num=None,
*args):
self.nature = nature
self.shape = shape
self.mtype = mtype
Model.__init__(self, name, default)
if mtype is not None and nature is not None and shape is not None:
self._preset = 1
if not self._preset:
if 'DWBA' in name:
self.mtype = 'THQ'
self.nature = 'ORG'
self.shape = ''
else:
self.mtype = input('Model type : THQ, EMP or HP?')
self.nature = input('What nature : ORG, MIN or HYB ?')
self.shape = input('What shape : SPH or sCYL or bCYL ?')
_L = {'rhos': rhos, 'c': c, 'cs': cs, 'cp': cp, 'mod_num': mod_num}
self.param.rhos = rhos
self.param.c = c
self.param.cs = cs
self.param.cp = cp
self.param.mod_num = mod_num
self.param_init()
[self.param.update({k: v}) for k, v in _L.items() if v is not None]
[docs] def param_init(self):
"""
Either fills the ``param`` modified dictionnary with the information
loaded from the xml file if the model already exists, or prompts the
user for extra information.
"""
if self._default is not None:
self.param.rhos = self.model_presets[self.name].default.rhos
self.param.cs = self.model_presets[self.name].default.cs
self.param.cp = self.model_presets[self.name].default.cp
self.param.c = np.sqrt(self.param.cp ** 2 -
(4 / 3) * self.param.cs ** 2)
self.param.mod_num = self.model_presets[self.name].default.mod_num
try:
self.param.m = self.model_presets[self.name].default.m
self.param.Cf = self.model_presets[self.name].default.Cf
except(AttributeError):
pass
try:
self.param.shape = self.model_presets[self.name].default.shape
self.param.simu = self.model_presets[self.name].default.simu
self.param.phy = self.model_presets[self.name].default.phy
except(AttributeError):
pass
else:
if None in list(self.param.values()):
inds = [i for (i, val) in enumerate(self.param.values()
) if val is None]
print(('{} are not attributed,' +
'please re-enter the parameters carefully'
).format([list(self.param.keys())[i] for i in inds]))
self.param.rhos = float(input('Density (kg/m^3): '))
if self.shape in ['CYL', 'PRO', 'bCYL']:
self.param.L_a = float(input('Ratio of Length to' +
'radius L/a:'))
if self.shape == 'bCYL':
self.param.rho_L = float(input('Ratio of radius of ' +
'curvature to Length ' +
'rho/L:'))
if self.shape in ['CYL', 'bCYL']:
self.param.ori = float(input('Angle in incident field ' +
'in degrees :'))
else:
self.param.ori = 0
if self.nature == 'ORG':
if self.param.cp is None:
self.param.cp = float(input('Sound velocity in the' +
'particle (m/s) :'))
self.param.cs = 0
self.param.c = self.param.cp
if 'DWBA' in self.name:
self.dwba_init()
elif self.nature == 'MIN':
if self.param.cp is None:
self.param.cp = float(input('Compressional velocity' +
' in the particle (m/s) :')
)
if self.param.cs is None:
self.param.cs = float(input('Shear velocity in the ' +
'particle (m/s) :'))
self.param.c = np.sqrt(self.param.cp ** 2 - (4 / 3) *
self.param.cs ** 2)
elif self.nature == 'HYB':
if self.param.cp is None:
self.param.cp = float(input('Compressional velocity' +
' in the elementary ' +
'particle (m/s) :'))
if self.param.cs is None:
self.param.cs = float(input('Shear velocity in the ' +
'elementary particle ' +
'(m/s) :'))
self.param.Cf = float(input('Cf for floc state :'))
self.param.m = float(input('m for fractal dimension :'))
self.param.c = np.sqrt(self.param.cp ** 2 - (4 / 3) *
self.param.cs ** 2)
elif self.nature == 'GAS':
if self.param.cp is None:
self.param.cp = float(input('Sound velocity in the' +
'particle (m/s) :'))
self.param.cs = 0
self.param.c = self.param.cp
else:
raise ParticleNatureError('Particle Nature not recognized')
if self.mtype in ['EMP', 'HP']:
self.param.mod_num = 0
else:
self.param.mod_num = int(input('Number of modes to ' +
'compute in the modal ' +
'series soltion ? :'))
if query_yes_no('Do you want to save the custom model ?'):
self._update_model_presets(self.name, {'mtype': self.mtype,
'nature': self.nature,
'shape': self.shape,
'default': self.param})
[docs] def dwba_init(self):
"""
This method prompts the user for the numerous parameters necessary to
compute a DWBA model. All the information is divided into three
categories, each taking the form of a modified dictionnary from the
class :class:`hydrac.util.parameters.AttrDict`:
- **shape** : Shape-related information about the animal such as
aspect ratio, preferential orientation angle and associated
standard deviation, radius of curvature in case of uniformly bent
cylinder...
- **phy** : Information related to the acoustic impedence contrast
between the animal and the water (ratio of densities, of sound
velocities, velocity of sound in water)
- **simu** : Information necessary for the numeriocal simulation such
as the number of integration points along the animal main axis,
the type of distribution of length and orientation for potential
averaging of the model over angle distribution and size
distribution...
"""
self.param.shape = Parameters({})
self.param.simu = Parameters({})
self.param.phy = Parameters({})
self.param.simu.model = float(input('scatt. model: ' +
'1-fluid, 2-gas, 3-shell :'))
self.param.simu.model_index = float(input('scat. model: 1-> BCM, ' +
'2->Medusae, ' +
'3->ellipsoid :'))
self.param.simu.min_ni = float(input('minimum integration points:'))
self.param.phy.g1 = float(input('density contrast: rho1/rhow:'))
self.param.phy.hL = float(input('compressional sound speed contrast:'))
self.param.phy.cw = float(input('sound speed in water'))
self.param.shape.L_a = float(input('L/a:'))
self.param.shape.ang = float(input('mean incident angle in degree:'))
self.param.shape.dang = float(input('standard deviation std(ang):'))
self.param.shape.L_b = float(input('L/b:'))
self.param.shape.phi = float(input('phi:'))
self.param.shape.rho_L = input('rho/L:')
if self.param.shape.rho_L != '':
self.param.shape.rho_L = float(self.param.shape.rho_L)
else:
self.param.shape.rho_L = None
self.param.shape.order = float(input('parameter controls tapering ' +
'funtion:'))
# Angle average
self.param.simu.aveA_flag = float(input('average over angle flag:'))
self.param.simu.aveA_PDF = float(input('DF model: 1-uniform, ' +
'2-Gaussian:'))
self.param.simu.nA = float(input('number of discrete As ' +
'to be averaged:'))
# Length average
self.param.simu.aveL_flag = float(input('average over length flag:'))
self.param.simu.aveL_PDF = float(input('PDF model: 1-uniform, ' +
'2-Gaussian:'))
self.param.simu.nL = float(input('number of discrete Ls to be ' +
'averaged:'))
if self.param.simu.aveA_flag == 1:
self.param.simu.ang = list(np.linspace(self.param.shape.ang-3 *
self.param.shape.dang,
self.param.shape.ang+3 *
self.param.shape.dang,
self.param.simu.nA))
print('ave ok {}'.format(self.param.simu.ang))
else:
self.param.simu.ang = self.param.shape.ang