#!/usr/bin/env python
"""This script/module contains routines that are used to analyze/visualize the data sets in the standard AVNI format."""
##################### IMPORT STANDARD MODULES ######################################
# python 3 compatibility
from __future__ import absolute_import, division, print_function
import numpy as np #for numerical analysis
####################### IMPORT AVNI LIBRARIES #######################################
from .. import tools
from .. import constants
#######################################################################################
# Vertical basis parameter class that defines an unique combination of functions, their radial parameterization
[docs]class Radial_basis(object):
"""A class for radial bases that defines a unique combination of parameters,
their radial parameterization and any scaling that is used.
Parameters
----------
object : _type_
object.data - Contains the following fields that describe
the radial basis.
depths_in_km: depth array in km
vercof: value of the bases evaluated at those depths
dvercof: gradient of the bases evaluated at those depths
object.metadata: Contains metadata for various calculations.
name: to store a name for the radial_basis
type: type of radial basis e.g. vbspl
attributes: a dictionary containing variables used to define this particular type
e.g. knots for vbspl. Checked that these are defined using self.check.
"""
######################### magic ##########################
def __init__(self,name,types,metadata=None):
self.data = {}
self.data['depths_in_km'] = None
self.data['vercof'] = None
self.data['dvercof'] = None
self._name = name
self._type = types
if metadata is None:
self.metadata = {}
else:
self.metadata = metadata
# Check if all required atributes are available
self.check()
def __eq__(self, other):
if not isinstance(other,Radial_basis): return False
# convert to array to allow multiple lateral bases to be compared
result = np.ones_like(other, dtype=bool)
# check if the instances have all required metadata
self.check()
try:
other.check()
except AttributeError: # if either is not this class instance
return False
# check type
if self._type != other._type: return False
# check all keys
for key in self.keys:
if not np.array_equal(self.metadata[key],other.metadata[key]): return False
# assume equal otherwise
return True
def __repr__(self):
return '{self.__class__.__name__}({self._name})'.format(self=self)
def __getitem__(self,key):
"""returns metadata from key"""
return self.metadata[key]
def __setitem__(self,key,data):
"""sets data to key"""
self.metadata[key] = data
######################### decorators ##########################
@property
def type(self):
return self._type
@property
def name(self):
return self._name
@property
def keys(self):
return self.metadata.keys()
######################### methods #############################
[docs] def add_attribute(self,key,value):
"""
Add attributes needed by the radial basis
Input parameters:
----------------
key: string key name
value: values corresponding to the key
"""
self.metadata[key] = value
[docs] def check(self):
"""
Checks that object contains all attributes required for evaluating a
particular basis set.
"""
if self._type in ['vbspl','variable splines']:
for key in ['knots']:
try:
knots = self.metadata[key]
except KeyError:
print('Current attributes : ',self.keys)
raise KeyError('Attribute '+key+' missing for radial basis type '+self._type)
elif self._type in ['delta','dirac delta']:
for key in ['info']:
try:
knots = self.metadata[key]
except KeyError:
print('Current attributes : ',self.keys)
raise KeyError('Attribute '+key+' missing for radial basis type '+self._type)
elif self._type in ['boxcar']:
for key in ['depthtop','depthbottom']:
try:
knots = self.metadata[key]
except KeyError:
print('Current attributes : ',self.keys)
raise KeyError('Attribute '+key+' missing for radial basis type '+self._type)
else:
raise TypeError('metadata type note defined in eval_radial %s' % self._type)
return knots
[docs] def eval_radial(self,depths_in_km,store=False):
"""
Evaluates the radial bases at various depths.
Input parameters:
----------------
depths_in_km: depths where the radial parameteriation needs to be evaluated.
store: store them in data
"""
# convert to numpy arrays
depths = tools.convert2nparray(depths_in_km)
# compute the radial parameteriation in specific depths
if self._type in ['vbspl','variable splines']:
knots = self.metadata['knots']
vercof, dvercof = tools.eval_vbspl(depths,knots)
elif self._type in ['delta','dirac delta']:
vercof = np.ones((len(depths),1))
dvercof = np.zeros((len(depths),1))
elif self._type in ['boxcar','constant']:
# convert to numpy arrays
depthtop = tools.convert2nparray(self.metadata['depthtop'])
depthbottom = tools.convert2nparray(self.metadata['depthbottom'])
rtop = constants.R.to('km').magnitude - depthtop
rbottom = constants.R.to('km').magnitude - depthbottom
rquery = constants.R.to('km').magnitude - depths
rrange = np.vstack((rbottom,rtop)).T
vercof, dvercof = tools.eval_polynomial(rquery,rrange,constants.R.to('km').magnitude,types = ['CONSTANT'])
else:
raise TypeError('metadata type not defined in eval_radial %s' % self._type)
# Store if needed
if store:
self.data['vercof'] = vercof
self.data['dvercof'] = dvercof
self.data['depths_in_km'] = depths
else:
return vercof,dvercof