# geotecha - A software suite for geotechncial engineering
# Copyright (C) 2018 Rohan T. Walker (rtrwalker@gmail.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/gpl.html.
"""
This module has functions classes and common functionality for one dimensinal
Spectral Galerkin methods.
"""
from __future__ import division, print_function
import numpy as np
import matplotlib.pyplot as plt
import geotecha.inputoutput.inputoutput as inputoutput
import geotecha.piecewise.piecewise_linear_1d as pwise
from geotecha.piecewise.piecewise_linear_1d import PolyLine
import geotecha.speccon.integrals as integ
[docs]class Speccon1d(inputoutput.InputFileLoaderCheckerSaver):
"""Solve 1D parabolic partial differential equation using spectral method.
Basically a base class to provide a broad template for one dimensional
spectral method consolidation problems.
See Also
--------
geotecha.inputoutput.InputFileLoaderCheckerSaver : Details on how to
initialize the object and attribute checks.
"""
[docs] def make_all(self):
"""Run checks, make all arrays, make output
Generally run this after attributes have been entered
See Also
--------
check_input_attributes
make_time_independent_arrays
make_time_dependent_arrays
make_output
"""
self.check_input_attributes()
self.make_time_independent_arrays()
self.make_time_dependent_arrays()
self.make_output()
if getattr(self, 'save_data_to_file', False):
self._save_data()
if (getattr(self, 'save_figures_to_file', False) or
getattr(self, 'show_figures', False)):
self.produce_plots()
if getattr(self, 'save_figures_to_file', False):
self._save_figures()
if getattr(self, 'show_figures', False):
plt.show()
return
[docs] def make_time_independent_arrays(self):
"""Make all time-independent arrays; To be overridden in subclasses."""
raise NotImplementedError("make_time_independent_arrays")
[docs] def make_time_dependent_arrays(self):
"""Make all time-independent arrays; To be overridden in subclasses."""
raise NotImplementedError("make_time_dependent_arrays")
[docs] def make_output(self):
"""Make all output i.e. data tables; To be overridden in subclasses."""
raise NotImplementedError("make_output")
[docs]def dim1sin_f(m,
outz,
tvals,
v_E_Igamv_the,
drn,
top_vs_time=None,
bot_vs_time=None,
top_omega_phase=None,
bot_omega_phase=None):
"""Assemble output u(Z,t) = phi * v_E_Igam_v_the + utop(t) * (1-Z) + ubot(t)*Z.
Basically calculates the phi part for each outz value, then dot product
with v_E_Igamv_the (which has elsewhere been calculated at each tvals
value). Then account for non-zero boundary conditions by
adding utop(t)*(1-Z) and ubot(t)*Z parts for each outz, tvals pair.
Use sin(m*Z) for the phi part.
Parameters
----------
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
outz : 1d numpy.ndarray
Depths to evaluate at.
tvals : 1d numpy.ndarray
Time values to evaluate at
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
v_E_Igamv_the : ndarray of size (neig, len(tvals))
Speccon matrix.
drn : [0,1]
Drainage condition,
drn=0 for Pervious top pervious bottom (PTPB).
drn=1 for Pervious top impoervious bottom (PTIB).
top_vs_time, bot_vs_time : list of PolyLine
Piecewise linear magnitude vs time for the top and bottom boundary.
Use ``None`` if there is no variation.
top_omega_phase, bot_omega_phase : list of 2 element tuples, optional
(omega, phase) for use in cos(omega * t + phase) * mag_vs_time
if omega_phase is None then mag_vs_time will not be multiplied by a
cosine. If any element of omega_phase is None then in that particular
loading combo, mag_vs_time will not be multiplied by a cosine.
Returns
-------
u : np.ndarray
Pore pressure at depth and time. Array of size (len(outz), len(tvals)).
Notes
-----
The :math:`\\phi` term is simply: :math:`\\sin\\left({m Z}\\right)`
evaluated at each required depth.
"""
phi = integ.dim1sin(m, outz)
u = np.dot(phi, v_E_Igamv_the)
#top part
if not top_vs_time is None:
if top_omega_phase is None:
top_omega_phase = [None] * len(top_vs_time)
for mag_vs_time, om_ph in zip(top_vs_time, top_omega_phase):
if not om_ph is None:
omega, phase = om_ph
mult = np.cos(omega * tvals + phase)
else:
mult = 1
if drn==1:
u += pwise.pinterp_x_y(mag_vs_time, tvals, choose_max=True) * mult
else:
u += pwise.pinterp_xa_ya_multipy_x1b_x2b_y1b_y2b(mag_vs_time, PolyLine([0], [1], [1], [0]), tvals, outz, achoose_max=True) * mult
#bot part
if not bot_vs_time is None:
if bot_omega_phase is None:
bot_omega_phase = [None] * len(bot_vs_time)
for mag_vs_time, om_ph in zip(bot_vs_time, bot_omega_phase):
if not om_ph is None:
omega, phase = om_ph
mult = np.cos(omega * tvals + phase)
else:
mult=1
u += pwise.pinterp_xa_ya_multipy_x1b_x2b_y1b_y2b(mag_vs_time, PolyLine([0], [1], [0], [1]), tvals, outz, achoose_max=True) * mult
return u
[docs]def dim1sin_avgf(m,
z,
tvals,
v_E_Igamv_the,
drn,
top_vs_time=None,
bot_vs_time=None,
top_omega_phase=None,
bot_omega_phase=None):
"""Average u(Z,t) between Z1 and Z2 where
u(Z,t) = phi * v_E_Igam_v_the + utop(t) * (1-Z) + ubot(t)*Z.
Basically calculates the average phi part for each z pair value, then dot product
with v_E_Igamv_the (which has elsewhere been calculated at each tvals
value). Then account for non-zero boundary conditions by
adding average of utop(t)*(1-Z) and ubot(t)*Z parts for each z, tvals pair.
Use sin(m*Z) for the phi part.
----------
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
z : size (n, 2) 2d numpy.ndarray
Depths to evaluate average between.
tvals : 1d numpy.ndarray
Time values to evaluate at.
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
v_E_Igamv_the : ndarray of size (neig, len(tvals))
Speccon matrix.
drn : [0,1]
Drainage condition,
drn=0 for Pervious top pervious bottom (PTPB).
drn=1 for Pervious top impoervious bottom (PTIB).
top_vs_time, bot_vs_time : list of PolyLine
Piecewise linear magnitude vs time for the top and bottom boundary.
Use ``None`` if there is no variation.
top_omega_phase, bot_omega_phase : list of 2 element tuples, optional
(omega, phase) for use in cos(omega * t + phase) * mag_vs_time
if omega_phase is None then mag_vs_time will not be multiplied by a
cosine. If any element of omega_phase is None then in that particular
loading combo, mag_vs_time will not be multiplied by a cosine.
Returns
-------
uavg : np.ndarray
Average pore pressure between depths at each time.
Array of size (len(z), len(tvals)).
Notes
-----
The average of the :math:`\\phi` term is:
.. math:: \\mathbf{\\phi}_{i\\textrm{average}}=
\\frac{1}{Z_2-Z_1}
\\int_{z_1}^{z_2}{\\sin\\left({m_i Z}\\right)\\,dZ}
"""
phi = integ.dim1sin_avg_between(m, z)
avg = np.dot(phi, v_E_Igamv_the)
z1 = np.asarray(z)[:,0]
z2 = np.asarray(z)[:,1]
#top part
if not top_vs_time is None:
if top_omega_phase is None:
top_omega_phase = [None] * len(top_vs_time)
for mag_vs_time, om_ph in zip(top_vs_time, top_omega_phase):
if not om_ph is None:
omega, phase = om_ph
mult = np.cos(omega * tvals + phase)
else:
mult = 1
if drn==1:
#bottom part
avg += pwise.pinterp_x_y(mag_vs_time, tvals, choose_max=True)*mult
else:
avg += pwise.pxa_ya_multipy_avg_x1b_x2b_y1b_y2b_between(mag_vs_time,
PolyLine([0], [1], [1], [0]),
tvals, z1, z2, achoose_max=True)*mult
#bottom part
if not bot_vs_time is None:
if bot_omega_phase is None:
bot_omega_phase = [None] * len(bot_vs_time)
for mag_vs_time, om_ph in zip(bot_vs_time, bot_omega_phase):
if not om_ph is None:
omega, phase = om_ph
mult = np.cos(omega * tvals + phase)
else:
mult=1
avg += pwise.pxa_ya_multipy_avg_x1b_x2b_y1b_y2b_between(mag_vs_time,
PolyLine([0], [1], [0], [1]),
tvals, z1,z2, achoose_max=True) * mult
return avg
[docs]def dim1sin_integrate_af(m,
z,
tvals,
v_E_Igamv_the,
drn,
a,
top_vs_time=None,
bot_vs_time=None,
top_omega_phase=None,
bot_omega_phase=None):
"""Integrate u(Z,t) between Z1 and Z2 where
u(Z,t) = phi * v_E_Igam_v_the + utop(t) * (1-Z) + ubot(t)*Z.
Basically calculates the integral phi part for each z pair value, then dot product
with v_E_Igamv_the (which has elsewhere been calculated at each tvals
value). Then account for non-zero boundary conditions by
adding average of utop(t)*(1-Z) and ubot(t)*Z parts for each z, tvals pair.
Use sin(m*Z) for the phi part.
Parameters
----------
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
z : size (n, 2) 2d numpy.ndarray
Depths to evaluate integral between.
tvals : 1d numpy.ndarray
Time values to evaluate at.
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
v_E_Igamv_the : ndarray of size (neig, len(tvals))
Speccon matrix.
drn : [0,1]
Drainage condition,
drn=0 for Pervious top pervious bottom (PTPB).
drn=1 for Pervious top impoervious bottom (PTIB).
top_vs_time, bot_vs_time : list of PolyLine
Piecewise linear magnitude vs time for the top and bottom boundary.
Use ``None`` if there is no variation.
top_omega_phase, bot_omega_phase : list of 2 element tuples, optional
(omega, phase) for use in cos(omega * t + phase) * mag_vs_time
if omega_phase is None then mag_vs_time will not be multiplied by a
cosine. If any element of omega_phase is None then in that particular
loading combo, mag_vs_time will not be multiplied by a cosine.
Returns
-------
uintegral : np.ndarray
Integral of pore pressure between depths at each time.
Array of size (len(z), len(tvals)).
Notes
-----
The integral of the :math:`\\phi` term is:
.. math:: \\mathbf{\\phi}_{i\\textrm{integral}}=
\\int_{z_1}^{z_2}{\\sin\\left({m_i Z}\\right)\\,dZ}
"""
z1 = np.array(z)[:,0]
z2 = np.array(z)[:,1]
#a*u part
phi = integ.pdim1sin_a_linear_between(m, a, z)
out = np.dot(phi, v_E_Igamv_the)
#top part
if not top_vs_time is None:
if top_omega_phase is None:
top_omega_phase = [None] * len(top_vs_time)
for mag_vs_time, om_ph in zip(top_vs_time, top_omega_phase):
if not om_ph is None:
omega, phase = om_ph
mult = np.cos(omega * tvals + phase)
else:
mult = 1
if drn==1:
out += mult * pwise.pxa_ya_multiply_integrate_x1b_x2b_y1b_y2b_multiply_x1c_x2c_y1c_y2c_between(
mag_vs_time,
a,
PolyLine(a.x1, a.x2, np.ones_like(a.x1), np.ones_like(a.x2)),
tvals, z1, z2, achoose_max=True)
else:
out += mult * pwise.pxa_ya_multiply_integrate_x1b_x2b_y1b_y2b_multiply_x1c_x2c_y1c_y2c_between(
mag_vs_time,
a,
PolyLine(a.x1, a.x2, 1-a.x1, 1-a.x2),
tvals, z1, z2, achoose_max=True)
#bot part
if not bot_vs_time is None:
if bot_omega_phase is None:
bot_omega_phase = [None] * len(bot_vs_time)
for mag_vs_time, om_ph in zip(bot_vs_time, bot_omega_phase):
if not om_ph is None:
omega, phase = om_ph
mult = np.cos(omega * tvals + phase)
else:
mult=1
out += mult * pwise.pxa_ya_multiply_integrate_x1b_x2b_y1b_y2b_multiply_x1c_x2c_y1c_y2c_between(
mag_vs_time,
a,
PolyLine(a.x1, a.x2, a.x1, a.x2),
tvals, z1, z2, achoose_max=True)
#self.set *= self.H * self.mvref
return out
[docs]def dim1sin_E_Igamv_the_BC_aDfDt_linear(drn,
m,
eigs,
tvals,
Igamv,
a,
top_vs_time,
bot_vs_time,
top_omega_phase=None,
bot_omega_phase=None,
dT=1.0,
theta_zero_indexes=None,
implementation='vectorized'):
"""Calculate E and theta parts and assemble E_Igamv_the matrix that arises
from homogenising a(Z)*D[u(Z, t), t] for non_zero top and bottom boundary
conditions.
When accounting for non-zero boundary conditions we homogenise the
governing equation by letting u(Z,t) = v(Z,t) + utop(t)*(1-Z) + ubot(t)*Z
and solving for v(Z, t). This function calculates the time dependent
E part, the depth dependent theta part, and then assembles
E*inverse(gam*v)*theta which forms part of solution
v(Z,t)=phi*v*E*inverse(gam*v)*theta. The E and theta parts arise
by subbing the boundary conditions into into governing equation terms of
the form a(z)*D[u(Z,t), t].
The contribution of each `mag_vs_time`-`omega_phase` pairing
are superposed. The result is an array
of size (neig, len(tvals)). So each column is the are the column vector
E*inverse(gam*v)*theta calculated at each output time. This will allow
us later to do v(Z,t) = phi*v*E_Igamv_the.
Uses sin(m*z) in the calculation of theta.
Parameters
----------
drn : [0,1]
Drainage condition,
drn=0 for Pervious top pervious bottom (PTPB).
drn=1 for Pervious top impoervious bottom (PTIB).
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
Igamv : ndarray
Speccon matrix. Igamv = inverse of [gam * v])
a : PolyLine
Piewcewise linear function. e.g. for 1d consolidation surcharge
loading term is mv*D[sigma(z, t), t] so `a` would be mv.
top_vs_time, bot_vs_time : list of PolyLine
Piecewise linear magnitude vs time for the top and bottom boundary.
Use ``None`` if there is no variation.
top_omega_phase, bot_omega_phase : list of 2 element tuples, optional
(omega, phase) for use in cos(omega * t + phase) * mag_vs_time
if omega_phase is None then mag_vs_time will not be multiplied by a
cosine. If any element of omega_phase is None then in that particular
loading combo, mag_vs_time will not be multiplied by a cosine.
dT : ``float``, optional
Time factor multiple for numerical convieniece. Default dT=1.0.
theta_zero_indexes : slice/list etc., optional
A slice object, list, etc that can be used for numpy fancy indexing.
Any specified index of the theta vector will be set to zero. This is
useful when using the spectral method with block matrices and the
loading term only refers to a subset of the equations. When using
block matrices m should be the same size as the block matrix.
Default theta_zero_indexes=None i.e. no elements of theta will be
set to zero.
Returns
-------
E_Igamv_the : ndarray
Loading matrix of size (neig, len(tvals)).
Notes
-----
Assuming the loads are formulated as the product of separate time and depth
dependant functions as well as a cyclic component:
.. math:: \\sigma\\left({Z,t}\\right)=
\\sigma\\left({Z}\\right)
\\sigma\\left({t}\\right)
\\cos\\left(\\omega t + \\phi\\right)
the solution to the consolidation equation using the spectral method has
the form:
.. math:: u\\left(Z,t\\right)=
\\mathbf{\\phi v E}
\\left(\\mathbf{\\Gamma v}\\right)^{-1}
\\mathbf{\\theta}
When we consider non-zero boundary conditions, additional loading terms are
created when we sub in the following into the original governing equation.
.. math:: u\\left({Z,t}\\right)=
v\\left({Z,t}\\right) +
u_{top}\\left({t}\\right)\\left({1-Z}\\right) +
u_{bot}\\left({b}\\right)Z
Two additional loading terms are created with each substitution, one
for the top boundary condition and one for the bottom boundary condition.
This function calculates :math:`\\mathbf{E}\\left(\\mathbf{\\Gamma v}\\right)^{-1}\\mathbf{\\theta}`
when substitutions are made in terms of the following form:
.. math:: a\\left({Z}\\right)\\frac{\\partial u}{\\partial t}
It is assumed that :math:`u_{top}\\left({t}\\right)` and
:math:`u_{bot}\\left({t}\\right)` are piecewise linear
in time with a cyclic component, and that multiple functions are
superposed. Also :math:`a\\left(Z\\right)`
is a piecewise linear function w.r.t. :math:`Z`
For this particular function the :math:`\\mathbf{\\theta}` vector for
each load is given by:
.. math:: \\mathbf{\\theta}_{i}=
\\int_{0}^1{
{a\\left(Z\\right)}
{\\sigma\\left(Z\\right)}
f\\left({Z}\\right)
\\phi_i\\,dZ}
Where :math:`f\\left({Z}\\right)` is the appropriate z-dependent term
corresponding to either :math:`u_{top}` or :math:`u_{bot}` homogenisations.
The :math:`\\mathbf{E}` matrix for each load is given by:
.. math:: \\mathbf{E}_{i,j}=
\\int_{0}^{t_j}{
\\frac{d{
{\\cos\\left(\\omega\\tau+\\textrm{phase}\\right)}
\\sigma\\left(\\tau\\right)}}
{d\\tau}
{\\exp\\left({(dT\\left(t-\\tau\\right)\\lambda_i}\\right)}
\\,d\\tau}
where
- :math:`\\lambda_i` is the `ith` eigenvalue of the problem,
- :math:`dT` is a time factor for numerical convienience,
- :math:`\\sigma\left(\\tau\\right)` is the piecewise linear time
dependant load.
Note that the listed equations above are in terms of normalised depth Z,
with depth integrations between [0, 1]. However, IF YOU KNOW WHAT YOU
ARE DOING the integrations can be done using non-normalised depths.
The first z value in the piecewise definition a(z) must still be 0
however the end point for integration will be the final z value in the
definition of a(z). If you are doing this then your `m` values will
include the normalising Factor. e.g. m = [pi/2/H, 3*pi/2/H] and a(z) is
defined in two layers [0, z1], [z1, zend] as opposed to
m = [pi/2, 3*pi/2] and a(Z) is two layers [0, z1/H], [z1/H, zend/H].
"""
E_Igamv_the = np.zeros((len(m), len(tvals)))
if not a is None:
if drn==1:
zdist = PolyLine(a.x1,a.x2, np.ones_like(a.x1), np.ones_like(a.x2))
#bot_vs_time=None
else:
zdist = PolyLine(a.x1,a.x2, a.x2[-1]-a.x1, a.x2[-1]-a.x2)
if not top_vs_time is None:
if top_omega_phase is None:
top_omega_phase = [None] * len(top_vs_time)
theta = integ.pdim1sin_ab_linear(m, a, zdist)
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
for top_vs_t, om_ph in zip(top_vs_time, top_omega_phase):
if not om_ph is None:
omega, phase = om_ph
E = integ.pEDload_coslinear(top_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEDload_linear(top_vs_t, eigs, tvals, dT, implementation=implementation)
E_Igamv_the += (E*np.dot(Igamv, theta)).T
if not bot_vs_time is None:
if bot_omega_phase is None:
bot_omega_phase = [None] * len(bot_vs_time)
theta = integ.pdim1sin_ab_linear(m, a, PolyLine(a.x1,a.x2,a.x1,a.x2))
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
for bot_vs_t, om_ph in zip(bot_vs_time, bot_omega_phase):
if not om_ph is None:
omega, phase = om_ph
E = integ.pEDload_coslinear(bot_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEDload_linear(bot_vs_t, eigs, tvals, dT, implementation=implementation)
E_Igamv_the += (E*np.dot(Igamv, theta)).T
#theta is 1d array, Igamv is nieg by neig array, np.dot(Igamv, theta)
#and np.dot(theta, Igamv) will give differetn 1d arrays.
#Basically np.dot(Igamv, theta) gives us what we want i.e.
#theta was treated as a column array. The alternative
#np.dot(theta, Igamv) would have treated theta as a row vector.
return E_Igamv_the
[docs]def dim1sin_E_Igamv_the_BC_abf_linear(drn,
m,
eigs,
tvals,
Igamv,
a,
b,
top_vs_time=None,
bot_vs_time=None,
top_omega_phase=None,
bot_omega_phase=None,
dT=1.0,
theta_zero_indexes=None,
implementation='vectorized'):
"""Calculate E and theta parts and assemble E_Igamv_the matrix that arises
from homogenising a(Z)*b(Z)*u(Z,t) for non_zero top and bottom boundary
conditions.
When accounting for non-zero boundary conditions we homogenise the
governing equation by letting u(Z,t) = v(Z,t) + utop(t)*(1-Z) + ubot(t)*Z
and solving for v(Z, t). This function calculates the time dependent
E part, the depth dependent theta part, and then assembles
E*inverse(gam*v)*theta which forms part of solution
v(Z,t)=phi*v*E*inverse(gam*v)*theta. The E and theta parts arise
by subbing the boundary conditions into into governing equation terms of
the form a(z)*b(z)*u(Z,t)
The contribution of each `mag_vs_time`-`omega_phase` pairing
are superposed. The result is an array
of size (neig, len(tvals)). So each column is the are the column vector
E*inverse(gam*v)*theta calculated at each output time. This will allow
us later to do v(Z,t) = phi*v*E_Igamv_the.
Uses sin(m*z) in the calculation of theta.
Parameters
----------
drn : [0,1]
Drainage condition,
drn=0 for Pervious top pervious bottom (PTPB).
drn=1 for Pervious top impoervious bottom (PTIB).
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
Igamv : ndarray
Speccon matrix. Igamv = inverse of [gam * v])
a, b : PolyLine
Piewcewise linear function. e.g. for 1d consolidation surcharge
radial draiange term is dTh*kh*et*u(Z,t) `a` would be kh, `b` would
be et.
top_vs_time, bot_vs_time : list of PolyLine
Piecewise linear magnitude vs time for the top and bottom boundary.
Use ``None`` if there is no variation.
top_omega_phase, bot_omega_phase : list of 2 element tuples, optional
(omega, phase) for use in cos(omega * t + phase) * mag_vs_time
if omega_phase is None then mag_vs_time will not be multiplied by a
cosine. If any element of omega_phase is None then in that particular
loading combo, mag_vs_time will not be multiplied by a cosine.
dT : ``float``, optional
Time factor multiple for numerical convieniece. Default dT=1.0.
theta_zero_indexes : slice/list etc., optional
A slice object, list, etc that can be used for numpy fancy indexing.
Any specified index of the theta vector will be set to zero. This is
useful when using the spectral method with block matrices and the
loading term only refers to a subset of the equations. When using
block matrices m should be the same size as the block matrix.
Default theta_zero_indexes=None i.e. no elements of theta will be
set to zero.
Returns
-------
E_Igamv_the : ndarray
Loading matrix of size (neig, len(tvals)).
Notes
-----
Assuming the loads are formulated as the product of separate time and depth
dependant functions as well as a cyclic component:
.. math:: \\sigma\\left({Z,t}\\right)=
\\sigma\\left({Z}\\right)
\\sigma\\left({t}\\right)
\\cos\\left(\\omega t + \\phi\\right)
the solution to the consolidation equation using the spectral method has
the form:
.. math:: u\\left(Z,t\\right)=
\\mathbf{\\phi v E}
\\left(\\mathbf{\\Gamma v}\\right)^{-1}
\\mathbf{\\theta}
When we consider non-zero boundary conditions, additional loading terms are
created when we sub in the following into the original governing equation.
.. math:: u\\left({Z,t}\\right)=
v\\left({Z,t}\\right) +
u_{top}\\left({t}\\right)\\left({1-Z}\\right) +
u_{bot}\\left({b}\\right)Z
Two additional loading terms are created with each substitution, one
for the top boundary condition and one for the bottom boundary condition.
This function calculates :math:`\\mathbf{E}\\left(\\mathbf{\\Gamma v}\\right)^{-1}\\mathbf{\\theta}`
when substitutions are made in terms of the following form:
.. math:: a\\left({Z}\\right)
b\\left({Z}\\right)
u\\left({Z,t}\\right)
It is assumed that :math:`u_{top}\\left({t}\\right)` and
:math:`u_{bot}\\left({t}\\right)` are piecewise linear
in time with a cyclic component, and that multiple functions are
superposed. Also :math:`a\\left(Z\\right)` and :math:`b\\left(Z\\right)`
are piecewise linear functions with respect to :math:`Z`
For this particular function the :math:`\\mathbf{\\theta}` vector for
each load is given by:
.. math:: \\mathbf{\\theta}_{i}=
\\int_{0}^1{
{a\\left(Z\\right)}
{b\\left(Z\\right)}
{\\sigma\\left(Z\\right)}
f\\left({Z}\\right)
\\phi_i\\,dZ}
Where :math:`f\\left({Z}\\right)` is the appropriate z-dependent term
corresponding to either :math:`u_{top}` or :math:`u_{bot}` homogenisations.
The :math:`\\mathbf{E}` matrix for each load is given by:
.. math:: \\mathbf{E}_{i,j}=
\\int_{0}^{t_j}{
{\\cos\\left(\\omega\\tau+\\textrm{phase}\\right)}
{\\sigma\\left(\\tau\\right)}
{\\exp\\left({(dT\\left(t-\\tau\\right)\\lambda_i}\\right)}
\\,d\\tau}
where
- :math:`\\lambda_i` is the `ith` eigenvalue of the problem,
- :math:`dT` is a time factor for numerical convienience,
- :math:`\\sigma\left(\\tau\\right)` is the piecewise linear time
dependant load.
Note that the listed equations above are in terms of normalised depth Z,
with depth integrations between [0, 1]. However, IF YOU KNOW WHAT YOU
ARE DOING the integrations can be done using non-normalised depths.
The first z value in the piecewise definition a(z) must still be 0
however the end point for integration will be the final z value in the
definition of a(z). If you are doing this then your `m` values will
include the normalising Factor. e.g. m = [pi/2/H, 3*pi/2/H] and a(z) is
defined in two layers [0, z1], [z1, zend] as opposed to
m = [pi/2, 3*pi/2] and a(Z) is two layers [0, z1/H], [z1/H, zend/H].
"""
E_Igamv_the = np.zeros((len(m), len(tvals)))
if sum([v is None for v in [a, b]]) == 0:
a, b = pwise.polyline_make_x_common(a, b)
if drn==1:
zdist = PolyLine(a.x1,a.x2, np.ones_like(a.x1), np.ones_like(a.x2))
#bot_vs_time=None
else:
zdist = PolyLine(a.x1,a.x2, a.x2[-1]-a.x1, a.x2[-1]-a.x2)
if not top_vs_time is None:
if top_omega_phase is None:
top_omega_phase = [None] * len(top_vs_time)
theta = integ.pdim1sin_abc_linear(m, a,b, zdist)
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
for top_vs_t, om_ph in zip(top_vs_time, top_omega_phase):
if not om_ph is None:
omega, phase = om_ph
E = integ.pEload_coslinear(top_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEload_linear(top_vs_t, eigs, tvals, dT, implementation=implementation)
# E_Igamv_the += (E*np.dot(Igamv, theta)).T
np.add(E_Igamv_the, (E*np.dot(Igamv, theta)).T,out=E_Igamv_the, casting='unsafe')
if not bot_vs_time is None:
if bot_omega_phase is None:
bot_omega_phase = [None] * len(bot_vs_time)
theta = integ.pdim1sin_abc_linear(m, a, b, PolyLine(a.x1,a.x2,a.x1,a.x2))
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
for bot_vs_t, om_ph in zip(bot_vs_time, bot_omega_phase):
if not om_ph is None:
omega, phase = om_ph
E = integ.pEload_coslinear(bot_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEload_linear(bot_vs_t, eigs, tvals, dT, implementation=implementation)
#E_Igamv_the += (E*np.dot(Igamv, theta)).T
np.add(E_Igamv_the, (E*np.dot(Igamv, theta)).T,out=E_Igamv_the, casting='unsafe')
#theta is 1d array, Igamv is nieg by neig array, np.dot(Igamv, theta)
#and np.dot(theta, Igamv) will give differetn 1d arrays.
#Basically np.dot(Igamv, theta) gives us what we want i.e.
#theta was treated as a column array. The alternative
#np.dot(theta, Igamv) would have treated theta as a row vector.
return E_Igamv_the
[docs]def dim1sin_E_Igamv_the_BC_abDfDt_linear(drn,
m,
eigs,
tvals,
Igamv,
a,
b,
top_vs_time=None,
bot_vs_time=None,
top_omega_phase=None,
bot_omega_phase=None,
dT=1.0,
theta_zero_indexes=None,
implementation='vectorized'):
"""Calculate E and theta parts and assemble E_Igamv_the matrix that arises
from homogenising a(Z)*b(Z)*D[u(Z, t), t] for non_zero top and bottom
boundary conditions.
When accounting for non-zero boundary conditions we homogenise the
governing equation by letting u(Z,t) = v(Z,t) + utop(t)*(1-Z) + ubot(t)*Z
and solving for v(Z, t). This function calculates the time dependent
E part, the depth dependent theta part, and then assembles
E*inverse(gam*v)*theta which forms part of solution
v(Z,t)=phi*v*E*inverse(gam*v)*theta. The E and theta parts arise
by subbing the boundary conditions into into governing equation terms of
the form a(Z)*b(Z)*D[u(Z,t), t].
The contribution of each `mag_vs_time`-`omega_phase` pairing
are superposed. The result is an array
of size (neig, len(tvals)). So each column is the are the column vector
E*inverse(gam*v)*theta calculated at each output time. This will allow
us later to do v(Z,t) = phi*v*E_Igamv_the.
Uses sin(m*Z) in the calculation of theta.
Parameters
----------
drn : [0,1]
Drainage condition,
drn=0 for Pervious top pervious bottom (PTPB).
drn=1 for Pervious top impoervious bottom (PTIB).
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
Igamv : ndarray
Speccon matrix. Igamv = inverse of [gam * v])
a, b : PolyLine
Piewcewise linear function. e.g. for 1d consolidation surcharge
radial draiange term is dTh*kh*et*u(Z,t) `a` would be kh, `b` would
be et.
top_vs_time, bot_vs_time : list of PolyLine
Piecewise linear magnitude vs time for the top and bottom boundary.
Use ``None`` if there is no variation.
top_omega_phase, bot_omega_phase : list of 2 element tuples, optional
(omega, phase) for use in cos(omega * t + phase) * mag_vs_time
if omega_phase is None then mag_vs_time will not be multiplied by a
cosine. If any element of omega_phase is None then in that particular
loading combo, mag_vs_time will not be multiplied by a cosine.
dT : ``float``, optional
Time factor multiple for numerical convieniece. Default dT=1.0.
theta_zero_indexes : slice/list etc., optional
A slice object, list, etc that can be used for numpy fancy indexing.
Any specified index of the theta vector will be set to zero. This is
useful when using the spectral method with block matrices and the
loading term only refers to a subset of the equations. When using
block matrices m should be the same size as the block matrix.
Default theta_zero_indexes=None i.e. no elements of theta will be
set to zero.
Returns
-------
E_Igamv_the : ndarray
Loading matrix of size (neig, len(tvals)).
Notes
-----
Assuming the loads are formulated as the product of separate time and depth
dependant functions as well as a cyclic component:
.. math:: \\sigma\\left({Z,t}\\right)=
\\sigma\\left({Z}\\right)
\\sigma\\left({t}\\right)
\\cos\\left(\\omega t + \\phi\\right)
the solution to the consolidation equation using the spectral method has
the form:
.. math:: u\\left(Z,t\\right)=
\\mathbf{\\phi v E}
\\left(\\mathbf{\\Gamma v}\\right)^{-1}
\\mathbf{\\theta}
When we consider non-zero boundary conditions, additional loading terms are
created when we sub in the following into the original governing equation.
.. math:: u\\left({Z,t}\\right)=
v\\left({Z,t}\\right) +
u_{top}\\left({t}\\right)\\left({1-Z}\\right) +
u_{bot}\\left({b}\\right)Z
Two additional loading terms are created with each substitution, one
for the top boundary condition and one for the bottom boundary condition.
This function calculates :math:`\\mathbf{E}\\left(\\mathbf{\\Gamma v}\\right)^{-1}\\mathbf{\\theta}`
when substitutions are made in terms of the following form:
.. math:: a\\left({Z}\\right)
b\\left({Z}\\right)
\\frac{\\partial u}{\\partial t}
It is assumed that :math:`u_{top}\\left({t}\\right)` and
:math:`u_{bot}\\left({t}\\right)` are piecewise linear
in time with a cyclic component, and that multiple functions are
superposed. Also :math:`a\\left(Z\\right)` and :math:`b\\left(Z\\right)`
are piecewise linear functions w.r.t. :math:`Z`
For this particular function the :math:`\\mathbf{\\theta}` vector for
each load is given by:
.. math:: \\mathbf{\\theta}_{i}=
\\int_{0}^1{
{a\\left(Z\\right)}
{b\\left(Z\\right)}
{\\sigma\\left(Z\\right)}
f\\left({Z}\\right)
\\phi_i\\,dZ}
Where :math:`f\\left({Z}\\right)` is the appropriate z-dependent term
corresponding to either :math:`u_{top}` or :math:`u_{bot}` homogenisations.
The :math:`\\mathbf{E}` matrix for each load is given by:
.. math:: \\mathbf{E}_{i,j}=
\\int_{0}^{t_j}{
\\frac{d{
{\\cos\\left(\\omega\\tau+\\textrm{phase}\\right)}
\\sigma\\left(\\tau\\right)}}
{d\\tau}
{\\exp\\left({(dT\\left(t-\\tau\\right)\\lambda_i}\\right)}
\\,d\\tau}
where
- :math:`\\lambda_i` is the `ith` eigenvalue of the problem,
- :math:`dT` is a time factor for numerical convienience,
- :math:`\\sigma\left(\\tau\\right)` is the piecewise linear time
dependant load.
Note that the listed equations above are in terms of normalised depth Z,
with depth integrations between [0, 1]. However, IF YOU KNOW WHAT YOU
ARE DOING the integrations can be done using non-normalised depths.
The first z value in the piecewise definition a(z) must still be 0
however the end point for integration will be the final z value in the
definition of a(z). If you are doing this then your `m` values will
include the normalising Factor. e.g. m = [pi/2/H, 3*pi/2/H] and a(z) is
defined in two layers [0, z1], [z1, zend] as opposed to
m = [pi/2, 3*pi/2] and a(Z) is two layers [0, z1/H], [z1/H, zend/H].
"""
E_Igamv_the = np.zeros((len(m), len(tvals)))
if sum([v is None for v in [a, b]]) == 0:
a, b = pwise.polyline_make_x_common(a, b)
if drn==1:
zdist = PolyLine(a.x1,a.x2, np.ones_like(a.x1), np.ones_like(a.x2))
#bot_vs_time=None
else:
zdist = PolyLine(a.x1,a.x2, a.x2[-1]-a.x1, a.x2[-1]-a.x2)
if not top_vs_time is None:
if top_omega_phase is None:
top_omega_phase = [None] * len(top_vs_time)
theta = integ.pdim1sin_abc_linear(m, a, b, zdist)
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
for top_vs_t, om_ph in zip(top_vs_time, top_omega_phase):
if not om_ph is None:
omega, phase = om_ph
E = integ.pEDload_coslinear(top_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEDload_linear(top_vs_t, eigs, tvals, dT, implementation=implementation)
E_Igamv_the += (E*np.dot(Igamv, theta)).T
if not bot_vs_time is None:
if bot_omega_phase is None:
bot_omega_phase = [None] * len(bot_vs_time)
theta = integ.pdim1sin_abc_linear(m, a, b, PolyLine(a.x1,a.x2,a.x1,a.x2))
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
for bot_vs_t, om_ph in zip(bot_vs_time, bot_omega_phase):
if not om_ph is None:
omega, phase = om_ph
E = integ.pEDload_coslinear(bot_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEDload_linear(bot_vs_t, eigs, tvals, dT, implementation=implementation)
E_Igamv_the += (E*np.dot(Igamv, theta)).T
#theta is 1d array, Igamv is nieg by neig array, np.dot(Igamv, theta)
#and np.dot(theta, Igamv) will give differetn 1d arrays.
#Basically np.dot(Igamv, theta) gives us what we want i.e.
#theta was treated as a column array. The alternative
#np.dot(theta, Igamv) would have treated theta as a row vector.
return E_Igamv_the
[docs]def dim1sin_E_Igamv_the_BC_D_aDf_linear(drn,
m,
eigs,
tvals,
Igamv,
a,
top_vs_time,
bot_vs_time,
top_omega_phase=None,
bot_omega_phase=None,
dT=1.0,
theta_zero_indexes=None,
implementation='vectorized'):
"""Calculate E and theta parts and assemble E_Igamv_the matrix that arises
from homogenising D[a(Z)*D[u(Z, t),Z],Z] for non_zero top and bottom
boundary conditions.
When accounting for non-zero boundary conditions we homogenise the
governing equation by letting u(Z,t) = v(Z,t) + utop(t)*(1-Z) + ubot(t)*Z
and solving for v(Z, t). This function calculates the time dependent
E part, the depth dependent theta part, and then assembles
E*inverse(gam*v)*theta which forms part of solution
v(Z,t)=phi*v*E*inverse(gam*v)*theta. The E and theta parts arise
by subbing the boundary conditions into into governing equation terms of
the form D[a(Z)*D[u(Z, t),Z],Z].
The contribution of each `mag_vs_time`-`omega_phase` pairing
are superposed. The result is an array
of size (neig, len(tvals)). So each column is the are the column vector
E*inverse(gam*v)*theta calculated at each output time. This will allow
us later to do v(Z,t) = phi*v*E_Igamv_the.
Uses sin(m*z) in the calculation of theta.
Parameters
----------
drn : [0,1]
Drainage condition,
drn=0 for Pervious top pervious bottom (PTPB).
drn=1 for Pervious top impoervious bottom (PTIB).
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
Igamv : ndarray
Speccon matrix. Igamv = inverse of [gam * v])
a : PolyLine
Piewcewise linear function. e.g. for 1d consolidation surcharge
radial draiange term is D[kv(z)*D[u(Z,t), Z],Z] so `a` would be kv.
be et.
top_vs_time, bot_vs_time : list of PolyLine
Piecewise linear magnitude vs time for the top and bottom boundary.
Use ``None`` if there is no variation.
top_omega_phase, bot_omega_phase : list of 2 element tuples, optional
(omega, phase) for use in cos(omega * t + phase) * mag_vs_time
if omega_phase is None then mag_vs_time will not be multiplied by a
cosine. If any element of omega_phase is None then in that particular
loading combo, mag_vs_time will not be multiplied by a cosine.
dT : ``float``, optional
Time factor multiple for numerical convieniece. Default dT=1.0.
theta_zero_indexes : slice/list etc., optional
A slice object, list, etc that can be used for numpy fancy indexing.
Any specified index of the theta vector will be set to zero. This is
useful when using the spectral method with block matrices and the
loading term only refers to a subset of the equations. When using
block matrices m should be the same size as the block matrix.
Default theta_zero_indexes=None i.e. no elements of theta will be
set to zero.
Returns
-------
E_Igamv_the : ndarray
Loading matrix of size (neig, len(tvals)).
Notes
-----
Assuming the loads are formulated as the product of separate time and depth
dependant functions as well as a cyclic component:
.. math:: \\sigma\\left({Z,t}\\right)=
\\sigma\\left({Z}\\right)
\\sigma\\left({t}\\right)
\\cos\\left(\\omega t + \\phi\\right)
the solution to the consolidation equation using the spectral method has
the form:
.. math:: u\\left(Z,t\\right)=
\\mathbf{\\phi} \\mathbf{v} \\mathbf{E}
\\left(\\mathbf{\\Gamma v}\\right)^{-1}
\\mathbf{\\theta}
When we consider non-zero boundary conditions, additional loading terms are
created when we sub in the following into the original governing equation.
.. math:: u\\left({Z,t}\\right)=
v\\left({Z,t}\\right) +
u_{top}\\left({t}\\right)\\left({1-Z}\\right) +
u_{bot}\\left({b}\\right)Z
Two additional loading terms are created with each substitution, one
for the top boundary condition and one for the bottom boundary condition.
This function calculates :math:`\\mathbf{E}\\left(\\mathbf{\\Gamma v}\\right)^{-1}\\mathbf{\\theta}`
when substitutions are made in terms of the following form:
.. math:: \\frac{\\partial}{\\partial Z}
\\left(
{a\\left({Z}\\right)
\\frac{\\partial u\\left({Z,t}\\right)}{\\partial Z}}
\\right)
It is assumed that :math:`u_{top}\\left({t}\\right)` and
:math:`u_{bot}\\left({t}\\right)` are piecewise linear
in time with a cyclic component, and that multiple functions are
superposed. Also :math:`a\\left(Z\\right)`
is a piecewise linear function with respect to :math:`Z`
For this particular function the :math:`\\mathbf{\\theta}` vector for
each load is given by:
.. math:: \\mathbf{\\theta}_{i}=
\\int_{0}^1{
\\frac{\\partial}{\\partial Z}
\\left(
{a\\left({Z}\\right)
\\frac{\\partial \\sigma\\left({Z}\\right)}{\\partial Z}}
\\right)
f\\left({Z}\\right)
\\phi_i\\,dZ}
Where :math:`f\\left({Z}\\right)` is the appropriate z-dependent term
corresponding to either :math:`u_{top}` or :math:`u_{bot}` homogenisations.
The :math:`\\mathbf{E}` matrix for each load is given by:
.. math:: \\mathbf{E}_{i,j}=
\\int_{0}^{t_j}{
{\\cos\\left(\\omega\\tau+\\textrm{phase}\\right)}
{\\sigma\\left(\\tau\\right)}
{\\exp\\left({(dT\\left(t-\\tau\\right)\\lambda_i}\\right)}
\\,d\\tau}
where
- :math:`\\lambda_i` is the `ith` eigenvalue of the problem,
- :math:`dT` is a time factor for numerical convienience,
- :math:`\\sigma\left(\\tau\\right)` is the piecewise linear time
dependant load.
Note that the listed equations above are in terms of normalised depth Z,
with depth integrations between [0, 1]. However, IF YOU KNOW WHAT YOU
ARE DOING the integrations can be done using non-normalised depths.
The first z value in the piecewise definition a(z) must still be 0
however the end point for integration will be the final z value in the
definition of a(z). If you are doing this then your `m` values will
include the normalising Factor. e.g. m = [pi/2/H, 3*pi/2/H] and a(z) is
defined in two layers [0, z1], [z1, zend] as opposed to
m = [pi/2, 3*pi/2] and a(Z) is two layers [0, z1/H], [z1/H, zend/H].
"""
E_Igamv_the = np.zeros((len(m), len(tvals)))
if not a is None:
if drn==1:
zdist = PolyLine(a.x1,a.x2, np.ones_like(a.x1), np.ones_like(a.x2))
#bot_vs_time=None
else:
zdist = PolyLine(a.x1,a.x2, a.x2[-1]-a.x1, a.x2[-1]-a.x2)
if not top_vs_time is None:
if top_omega_phase is None:
top_omega_phase = [None] * len(top_vs_time)
theta = integ.pdim1sin_D_aDb_linear(m, a, zdist)
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
for top_vs_t, om_ph in zip(top_vs_time, top_omega_phase):
if not om_ph is None:
omega, phase = om_ph
E = integ.pEload_coslinear(top_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEload_linear(top_vs_t, eigs, tvals, dT, implementation=implementation)
# E_Igamv_the += (E*np.dot(Igamv, theta)).T
np.add(E_Igamv_the, (E*np.dot(Igamv, theta)).T,out=E_Igamv_the, casting='unsafe')
if not bot_vs_time is None:
if bot_omega_phase is None:
bot_omega_phase = [None] * len(bot_vs_time)
theta = integ.pdim1sin_D_aDb_linear(m, a, PolyLine(a.x1,a.x2,a.x1,a.x2))
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
for bot_vs_t, om_ph in zip(bot_vs_time, bot_omega_phase):
if not om_ph is None:
omega, phase = om_ph
E = integ.pEload_coslinear(bot_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEload_linear(bot_vs_t, eigs, tvals, dT, implementation=implementation)
np.add(E_Igamv_the, (E*np.dot(Igamv, theta)).T,out=E_Igamv_the, casting='unsafe')
# E_Igamv_the += (E*np.dot(Igamv, theta)).T
#theta is 1d array, Igamv is nieg by neig array, np.dot(Igamv, theta)
#and np.dot(theta, Igamv) will give differetn 1d arrays.
#Basically np.dot(Igamv, theta) gives us what we want i.e.
#theta was treated as a column array. The alternative
#np.dot(theta, Igamv) would have treated theta as a row vector.
return E_Igamv_the
[docs]def dim1sin_E_Igamv_the_BC_deltaf_linear(drn,
m,
eigs,
tvals,
Igamv,
zvals,
pseudo_k,
top_vs_time,
bot_vs_time,
top_omega_phase=None,
bot_omega_phase=None,
dT=1.0,
theta_zero_indexes=None,
implementation='vectorized'):
"""Calculate E and theta parts and assemble E_Igamv_the matrix that arises
from homogenising delta(Z-zd)*u(Z,t) for non_zero top and bottom
boundary conditions.
When accounting for non-zero boundary conditions we homogenise the
governing equation by letting u(Z,t) = v(Z,t) + utop(t)*(1-Z) + ubot(t)*Z
and solving for v(Z, t). This function calculates the time dependent
E part, the depth dependent theta part, and then assembles
E*inverse(gam*v)*theta which forms part of solution
v(Z,t)=phi*v*E*inverse(gam*v)*theta. The E and theta parts arise
by subbing the boundary conditions into into governing equation terms of
the form delta(Z-zd)*u(Z,t).
The contribution of each `mag_vs_time`-`omega_phase` pairing
are superposed. The result is an array
of size (neig, len(tvals)). So each column is the are the column vector
E*inverse(gam*v)*theta calculated at each output time. This will allow
us later to do v(Z,t) = phi*v*E_Igamv_the.
Uses sin(m*z) in the calculation of theta.
Parameters
----------
drn : [0,1]
Drainage condition,
drn=0 for Pervious top pervious bottom (PTPB).
drn=1 for Pervious top impoervious bottom (PTIB).
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
Igamv : ndarray
Speccon matrix. Igamv = inverse of [gam * v])
zvals : list of float
z values defining each delta function, zd.
pseudo_k : list of float
Coefficients to multiply each delta function by.
top_vs_time, bot_vs_time : list of PolyLine
Piecewise linear magnitude vs time for the top and bottom boundary.
Use ``None`` if there is no variation.
top_omega_phase, bot_omega_phase : list of 2 element tuples, optional
(omega, phase) for use in cos(omega * t + phase) * mag_vs_time
if omega_phase is None then mag_vs_time will not be multiplied by a
cosine. If any element of omega_phase is None then in that particular
loading combo, mag_vs_time will not be multiplied by a cosine.
dT : ``float``, optional
Time factor multiple for numerical convieniece. Default dT=1.0.
theta_zero_indexes : slice/list etc., optional
A slice object, list, etc that can be used for numpy fancy indexing.
Any specified index of the theta vector will be set to zero. This is
useful when using the spectral method with block matrices and the
loading term only refers to a subset of the equations. When using
block matrices m should be the same size as the block matrix.
Default theta_zero_indexes=None i.e. no elements of theta will be
set to zero.
Returns
-------
E_Igamv_the : ndarray
Loading matrix of size (neig, len(tvals)).
Notes
-----
Assuming the loads are formulated as the product of separate time and depth
dependant functions as well as a cyclic component:
.. math:: \\sigma\\left({Z,t}\\right)=
\\sigma\\left({Z}\\right)
\\sigma\\left({t}\\right)
\\cos\\left(\\omega t + \\phi\\right)
the solution to the consolidation equation using the spectral method has
the form:
.. math:: u\\left(Z,t\\right)=
\\mathbf{\\phi v E}
\\left(\\mathbf{\\Gamma v}\\right)^{-1}
\\mathbf{\\theta}
When we consider non-zero boundary conditions, additional loading terms are
created when we sub in the following into the original governing equation.
.. math:: u\\left({Z,t}\\right)=
v\\left({Z,t}\\right) +
u_{top}\\left({t}\\right)\\left({1-Z}\\right) +
u_{bot}\\left({b}\\right)Z
Two additional loading terms are created with each substitution, one
for the top boundary condition and one for the bottom boundary condition.
This function calculates :math:`\\mathbf{E}\\left(\\mathbf{\\Gamma v}\\right)^{-1}\\mathbf{\\theta}`
when substitutions are made in terms of the following form:
.. math:: k_{\\textrm{pseudo}}
\\delta\\left({Z-Z_d}\\right)
u\\left({Z,t}\\right)
It is assumed that :math:`u_{top}\\left({t}\\right)` and
:math:`u_{bot}\\left({t}\\right)` are piecewise linear
in time with a cyclic component, and that multiple functions are
superposed.
For this particular function the :math:`\\mathbf{\\theta}` vector for
each load is given by:
.. math:: \\mathbf{\\theta}_{i}=
\\int_{0}^1{
k_{\\textrm{pseudo}}
\\delta\\left({Z-Z_d}\\right)
{\\sigma\\left(Z\\right)}
f\\left({Z}\\right)
\\phi_i\\,dZ}
Where :math:`f\\left({Z}\\right)` is the appropriate z-dependent term
corresponding to either :math:`u_{top}` or :math:`u_{bot}` homogenisations.
The :math:`\\mathbf{E}` matrix for each load is given by:
.. math:: \\mathbf{E}_{i,j}=
\\int_{0}^{t_j}{
{\\cos\\left(\\omega\\tau+\\textrm{phase}\\right)}
{\\sigma\\left(\\tau\\right)}
{\\exp\\left({(dT\\left(t-\\tau\\right)\\lambda_i}\\right)}
\\,d\\tau}
where
- :math:`\\lambda_i` is the `ith` eigenvalue of the problem,
- :math:`dT` is a time factor for numerical convienience,
- :math:`\\sigma\left(\\tau\\right)` is the piecewise linear time
dependant load.
Note that this function, unlike many similar functions in this module,
has only been formulated for Normalised depths between [0,1]
"""
E_Igamv_the = np.zeros((len(m), len(tvals)))
zvals = np.asarray(zvals)
if drn==1:
zdist = np.ones_like(zvals)
#bot_vs_time=None
else:
zdist = 1.0 - zvals
zdist = 1 - zvals * (1 - drn)
if not top_vs_time is None:
if top_omega_phase is None:
top_omega_phase = [None] * len(top_vs_time)
for top_vs_t, om_ph in zip(top_vs_time, top_omega_phase):
if not om_ph is None:
omega, phase = om_ph
E = integ.pEload_coslinear(top_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEload_linear(top_vs_t, eigs, tvals, dT, implementation=implementation)
for z, zd, k in zip(zvals, zdist, pseudo_k):
theta = k * np.sin(z * m) * zd
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
# E_Igamv_the += (E*np.dot(Igamv, theta)).T
np.add(E_Igamv_the, (E*np.dot(Igamv, theta)).T,out=E_Igamv_the, casting='unsafe')
if not bot_vs_time is None:
if bot_omega_phase is None:
bot_omega_phase = [None] * len(bot_vs_time)
for bot_vs_t, om_ph in zip(bot_vs_time, bot_omega_phase):
if not om_ph is None:
omega, phase = om_ph
E = integ.pEload_coslinear(bot_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEload_linear(bot_vs_t, eigs, tvals, dT, implementation=implementation)
for z, k in zip(zvals, pseudo_k):
theta = k * np.sin(z * m) * z
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
# E_Igamv_the += (E*np.dot(Igamv, theta)).T
np.add(E_Igamv_the, (E*np.dot(Igamv, theta)).T,out=E_Igamv_the, casting='unsafe')
#theta is 1d array, Igamv is nieg by neig array, np.dot(Igamv, theta)
#and np.dot(theta, Igamv) will give differetn 1d arrays.
#Basically np.dot(Igamv, theta) gives us what we want i.e.
#theta was treated as a column array. The alternative
#np.dot(theta, Igamv) would have treated theta as a row vector.
return E_Igamv_the
[docs]def dim1sin_E_Igamv_the_deltamag_linear(m,
eigs,
tvals,
Igamv,
zvals,
pseudo_k,
mag_vs_time,
omega_phase=None,
dT=1.0,
theta_zero_indexes=None,
implementation='vectorized'):
"""Calculate E and theta parts and assemble E_Igamv_the matrix
for loading terms of the form delta(Z-Zd)*mag(t) where mag is
piecewise linear in time multiplied by cos(omega * t + phase).
Make the E*inverse(gam*v)*theta part of solution
u(Z,t)=phi*v*E*inverse(gam*v)*theta for terms of the form
k*delta(Z-Zd)*mag(t).
The contribution of each `mag_vs_time`-`omega_phase` pairing and each zval
are superposed. The result is an array
of size (neig, len(tvals)). So each column is the are the column vector
E*inverse(gam*v)*theta calculated at each output time. This will allow
us later to do u(Z,t) = phi*v*E_Igamv_the.
Uses sin(m*Z) in the calculation of theta.
Parameters
----------
drn : [0,1]
Drainage condition,
drn=0 for Pervious top pervious bottom (PTPB).
drn=1 for Pervious top impoervious bottom (PTIB).
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
Igamv : ndarray
Speccon matrix. Igamv = inverse of [gam * v])
zvals : list of float
z values defining each delta function, zd.
pseudo_k : list of float
Coefficients to multiply each delta function by.
dT : ``float``, optional
Time factor multiple for numerical convieniece. Default dT=1.0.
theta_zero_indexes : slice/list etc., optional
A slice object, list, etc that can be used for numpy fancy indexing.
Any specified index of the theta vector will be set to zero. This is
useful when using the spectral method with block matrices and the
loading term only refers to a subset of the equations. When using
block matrices m should be the same size as the block matrix.
Default theta_zero_indexes=None i.e. no elements of theta will be
set to zero.
Returns
-------
E_Igamv_the : ndarray
Loading matrix of size (neig, len(tvals)).
Notes
-----
Assuming the loads are formulated as the product of separate time and depth
dependant functions as well as a cyclic component:
.. math:: \\sigma\\left({Z,t}\\right)=
\\sigma\\left({Z}\\right)
\\sigma\\left({t}\\right)
\\cos\\left(\\omega t + \\phi\\right)
the solution to the consolidation equation using the spectral method has
the form:
.. math:: u\\left(Z,t\\right)=
\\mathbf{\\phi v E}
\\left(\\mathbf{\\Gamma v}\\right)^{-1}
\\mathbf{\\theta}
This function calculates :math:`\\mathbf{E}\\left(\\mathbf{\\Gamma v}\\right)^{-1}\\mathbf{\\theta}`
for terms of the following form:
.. math:: k_{\\textrm{pseudo}}
\\delta\\left({Z-Z_d}\\right)
\\sigma\\left({t}\\right)
It is assumed that :math:`\\sigma\\left({t}\\right)` is piecewise linear
in time with a cyclic component, and that multiple functions are
superposed.
For this particular function the :math:`\\mathbf{\\theta}` vector for
each load is given by:
.. math:: \\mathbf{\\theta}_{i}=
\\int_{0}^1{
k_{\\textrm{pseudo}}
\\delta\\left({Z-Z_d}\\right)
\\phi_i\\,dZ}
The :math:`\\mathbf{E}` matrix for each load is given by:
.. math:: \\mathbf{E}_{i,j}=
\\int_{0}^{t_j}{
{\\cos\\left(\\omega\\tau+\\textrm{phase}\\right)}
{\\sigma\\left(\\tau\\right)}
{\\exp\\left({(dT\\left(t-\\tau\\right)\\lambda_i}\\right)}
\\,d\\tau}
where
- :math:`\\lambda_i` is the `ith` eigenvalue of the problem,
- :math:`dT` is a time factor for numerical convienience,
- :math:`\\sigma\left(\\tau\\right)` is the piecewise linear time
dependant load.
"""
E_Igamv_the = np.zeros((len(m), len(tvals)), dtype=complex)
if omega_phase is None:
omega_phase = [None] * len(mag_vs_time)
for z, k, mag_vs_t, om_ph in zip(zvals, pseudo_k, mag_vs_time, omega_phase):
if mag_vs_t is None:
continue
theta = k * np.sin(z * m)
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
if not om_ph is None:
omega, phase = om_ph
E = integ.pEload_coslinear(mag_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEload_linear(mag_vs_t, eigs, tvals, dT, implementation=implementation)
E_Igamv_the += (E*np.dot(Igamv, theta)).T
return E_Igamv_the
[docs]def dim1sin_E_Igamv_the_aDmagDt_bilinear(m,
eigs,
tvals,
Igamv,
a,
mag_vs_depth,
mag_vs_time,
omega_phase=None,
dT=1.0,
theta_zero_indexes=None,
implementation='vectorized'):
"""Calculate E and theta parts and assemble E_Igamv_the matrix
for loading terms of the form a(Z) * D[mag(t, Z),t] where mag is
piecewise linear in depth and time and multiplied by cos(omega * t + phase).
Make the E*inverse(gam*v)*theta part of solution
u(Z,t)=phi*v*E*inverse(gam*v)*theta for terms of the form
a(Z) * D[mag(t, Z),t].
The contribution of each `mag_vs_time`-`omega_phase` pairing and
are superposed. The result is an array
of size (neig, len(tvals)). So each column is the column vector
E*inverse(gam*v)*theta calculated at each output time. This will allow
us later to do u(Z,t) = phi*v*E_Igamv_the.
Uses sin(m*z) in the calculation of theta.
Parameters
----------
drn : [0,1]
Drainage condition,
drn=0 for Pervious top pervious bottom (PTPB).
drn=1 for Pervious top impoervious bottom (PTIB).
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
Igamv : ndarray
Speccon matrix. Igamv = inverse of [gam * v])
a : PolyLine
Piewcewise linear function. e.g. for 1d consolidation surcharge
loading term is mv*D[sigma(z, t), t] so `a` would be mv.
dT : ``float``, optional
Time factor multiple for numerical convieniece. Default dT=1.0.
theta_zero_indexes : slice/list etc., optional
A slice object, list, etc that can be used for numpy fancy indexing.
Any specified index of the theta vector will be set to zero. This is
useful when using the spectral method with block matrices and the
loading term only refers to a subset of the equations. When using
block matrices m should be the same size as the block matrix.
Default theta_zero_indexes=None i.e. no elements of theta will be
set to zero.
Returns
-------
E_Igamv_the : ndarray
Loading matrix of size (neig, len(tvals)).
Notes
-----
Assuming the loads are formulated as the product of separate time and depth
dependant functions as well as a cyclic component:
.. math:: \\sigma\\left({Z,t}\\right)=
\\sigma\\left({Z}\\right)
\\sigma\\left({t}\\right)
\\cos\\left(\\omega t + \\phi\\right)
the solution to the consolidation equation using the spectral method has
the form:
.. math:: u\\left(Z,t\\right)=
\\mathbf{\\phi v E}
\\left(\\mathbf{\\Gamma v}\\right)^{-1}
\\mathbf{\\theta}
This function calculates :math:`\\mathbf{E}\\left(\\mathbf{\\Gamma v}\\right)^{-1}\\mathbf{\\theta}`
for terms of the following form:
.. math:: a\\left({Z}\\right)\\frac{\\partial \\sigma}{\\partial t}
It is assumed that :math:`\\sigma\\left({t}\\right)` is piecewise linear
in time with a cyclic component, and that multiple functions are
superposed. Also :math:`a\\left(Z\\right)`
is a piecewise linear function with respect to :math:`Z`.
For this particular function the :math:`\\mathbf{\\theta}` vector for
each load is given by:
.. math:: \\mathbf{\\theta}_{i}=
\\int_{0}^1{
{a\\left(Z\\right)}
{\\sigma\\left(Z\\right)}
\\phi_i\\,dZ}
The :math:`\\mathbf{E}` matrix for each load is given by:
.. math:: \\mathbf{E}_{i,j}=
\\int_{0}^{t_j}{
\\frac{d{
{\\cos\\left(\\omega\\tau+\\textrm{phase}\\right)}
\\sigma\\left(\\tau\\right)}}
{d\\tau}
{\\exp\\left({(dT\\left(t-\\tau\\right)\\lambda_i}\\right)}
\\,d\\tau}
where
- :math:`\\lambda_i` is the `ith` eigenvalue of the problem,
- :math:`dT` is a time factor for numerical convienience,
- :math:`\\sigma\left(\\tau\\right)` is the piecewise linear time
dependant load.
Note that the listed equations above are in terms of normalised depth Z,
with depth integrations between [0, 1]. However, IF YOU KNOW WHAT YOU
ARE DOING the integrations can be done using non-normalised depths.
The first z value in the piecewise definition a(z) must still be 0
however the end point for integration will be the final z value in the
definition of a(z). If you are doing this then your `m` values will
include the normalising Factor. e.g. m = [pi/2/H, 3*pi/2/H] and a(z) is
defined in two layers [0, z1], [z1, zend] as opposed to
m = [pi/2, 3*pi/2] and a(Z) is two layers [0, z1/H], [z1/H, zend/H].
"""
E_Igamv_the = np.zeros((len(m), len(tvals)))
if sum([v is None for v in [mag_vs_depth, mag_vs_time]])==0:
if omega_phase is None:
omega_phase = [None] * len(mag_vs_time)
for mag_vs_t, mag_vs_z, om_ph in zip(mag_vs_time, mag_vs_depth, omega_phase):
a, mag_vs_z = pwise.polyline_make_x_common(a, mag_vs_z)
theta = integ.pdim1sin_ab_linear(m, a, mag_vs_z)
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
if not om_ph is None:
omega, phase = om_ph
E = integ.pEDload_coslinear(mag_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEDload_linear(mag_vs_t, eigs, tvals, dT, implementation=implementation)
#theta is 1d array, Igamv is nieg by neig array, np.dot(Igamv, theta)
#and np.dot(theta, Igamv) will give differetn 1d arrays.
#Basically np.dot(Igamv, theta) gives us what we want i.e.
#theta was treated as a column array. The alternative
#np.dot(theta, Igamv) would have treated theta as a row vector.
E_Igamv_the += (E*np.dot(Igamv, theta)).T
return E_Igamv_the
[docs]def dim1sin_E_Igamv_the_abmag_bilinear(m,
eigs,
tvals,
Igamv,
a,
b,
mag_vs_depth,
mag_vs_time,
omega_phase=None,
dT=1.0,
theta_zero_indexes=None,
implementation='vectorized'):
"""Calculate E and theta parts and assemble E_Igamv_the matrix
for loading terms of the form a(Z)*b(Z)*mag(t, Z) where mag is
piecewise linear in depth and time and multiplied by cos(omega * t + phase).
Make the E*inverse(gam*v)*theta part of solution u=phi*v*E*inverse(gam*v)*theta.
The contribution of each `mag_vs_time`-`mag_vs_depth`-`omega_phase` pair are superposed.
The result is an array
of size (neig, len(tvals)). So the columns are the column array
E*inverse(gam*v)*theta calculated at each output time. This will allow
us later to do u = phi*v*E_Igamv_the
Uses sin(m*z) in the calculation of theta.
Parameters
----------
m : ``list`` of ``float``
eigenvlaues of BVP. generate with geoteca.speccon.m_from_sin_mx
eigs : 1d numpy.ndarray
list of eigenvalues
tvals : 1d numpy.ndarray`
list of time values to calculate integral at
Igamv : ndarray
speccon matrix
a : PolyLine
Piewcewise linear function. e.g. for 1d consolidation vacuum term is
kh*et*w(z,t) so a would be `kh`, `b` would be `et`
b : PolyLine
Piewcewise linear function. e.g. for 1d consolidation vacuum term is
kh*et*w(z,t) so a would be `kh`, `b` would be `et`
mag_vs_depth : list of PolyLine
Piecewise linear magnitude vs depth.
mag_vs_time : list of PolyLine
Piecewise linear magnitude vs time
omega_phase : list of 2 element tuples, optional
(omega, phase) for use in cos(omega * t + phase) * mag_vs_time
if omega_phase is None then mag_vs_time will not be multiplied by a
cosine. If any element of omega_phase is None then in that particular
loading combo, mag_vs_time will not be multiplied by a cosine.
dT : ``float``, optional
time factor multiple (default = 1.0)
theta_zero_indexes : slice/list etc., optional=None
a slice object, list, etc that can be used for numpy fancy indexing.
Any specified index of the theta vector will be set to zero. This is
useful when using the spectral method with block matrices and the
loading term only refers to a subset of the equations. When using
block matrices m should be the same size as the block matrix.
default=None i.e. no elements of theta will be set to zero.
Returns
-------
E_Igamv_the: ndarray
loading matrix
Notes
-----
Assuming the loads are formulated as the product of separate time and depth
dependant functions as well as a cyclic component:
.. math:: \\sigma\\left({Z,t}\\right)=\\sigma\\left({Z}\\right)\\sigma\\left({t}\\right)\\cos\\left(\\omega t + \\phi\\right)
the solution to the consolidation equation using the spectral method has
the form:
.. math:: u\\left(Z,t\\right)=\\mathbf{\\Phi v E}\\left(\\mathbf{\\Gamma v}\\right)^{-1}\\mathbf{\\theta}
In this instance :math:`\\sigma\\left({Z}\\right)`
and :math:`\\sigma\\left({t}\\right)` are piecewise linear in depth and
time (hence the 'bilinear' in the function name) there is also a cyclic
component.
`dim1sin_E_Igamv_the_abmag_bilinear` will calculate
:math:`\\mathbf{E}\\left(\\mathbf{\\Gamma v}\\right)^{-1}\\mathbf{\\theta}`
for terms with the form:
.. math:: a\\left({z}\\right)b\\left({z}\\right)\\frac{\\partial\\sigma\\left({Z,t}\\right)}{\\partial t}
where :math:`a\\left(z\\right)`, :math:`b\\left(z\\right)` are
piecewise linear functions w.r.t. :math:`z`.
"""
E_Igamv_the = np.zeros((len(m), len(tvals)))
if sum([v is None for v in [mag_vs_depth, mag_vs_time]])==0:
if omega_phase is None:
omega_phase = [None] * len(mag_vs_time)
for mag_vs_t, mag_vs_z, om_ph in zip(mag_vs_time, mag_vs_depth, omega_phase):
a, b , mag_vs_z = pwise.polyline_make_x_common(a, b, mag_vs_z)
theta = integ.pdim1sin_abc_linear(m, a, b, mag_vs_z)
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
if not om_ph is None:
omega, phase = om_ph
E = integ.pEload_coslinear(mag_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
else:
E = integ.pEload_linear(mag_vs_t, eigs, tvals, dT, implementation=implementation)
#theta is 1d array, Igamv is nieg by neig array, np.dot(Igamv, theta)
#and np.dot(theta, Igamv) will give differetn 1d arrays.
#Basically np.dot(Igamv, theta) gives us what we want i.e.
#theta was treated as a column array. The alternative
#np.dot(theta, Igamv) would have treated theta as a row vector.
# E_Igamv_the += (E*np.dot(Igamv, theta)).T
np.add(E_Igamv_the, (E*np.dot(Igamv, theta)).T,out=E_Igamv_the, casting='unsafe')
return E_Igamv_the
[docs]def dim1sin_foft_Ipsiw_the_BC_D_aDf_linear(drn,
m,
eigs,
tvals,
Ipsiw,
a,
top_vs_time,
bot_vs_time,
top_omega_phase=None,
bot_omega_phase=None,
theta_zero_indexes=None):
"""Calculate the f(t) and theta parts and assemble the foft_Ipsiw_the
matrix that arises from homgenising D[a(Z)*D[u(Z, t),Z],Z] terms with
non_zero top and bottom boundary conditions when modelling
drains/wells/columns with finite permeability.
When accounting for non-zero boundary conditions we homogenise the
governing equation by letting u(Z,t) = v(Z,t) + utop(t)*(1-Z) + ubot(t)*Z
and solving for v(Z, t). For some problems the homogenisation process
produces an additional term that has t be added to the usual solution of
v(Z,t)=phi*v*E*inverse(gam*v)*theta
i.e. v(Z,t)=phi*v*E*inverse(gam*v)*theta + f(t)*Ipsiw*theta
This function calculates the f(t) and theta parts of the second term and
then assembles the foft_Ipsiw_the matrix. These parts arise
by subbing the boundary conditions into into governing equation terms of
the form D[a(Z)*D[u(Z, t),Z],Z].
Uses sin(m*Z) in the calculation of theta.
Parameters
----------
drn : [0,1]
Drainage condition,
drn=0 for Pervious top pervious bottom (PTPB).
drn=1 for Pervious top impoervious bottom (PTIB).
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
Ipsiw : ndarray, square matrix
Speccon matrix. Ipsiw was originally
formulated/denoted for vertical peremability term in the well
resistance flow equation for vertical drain consolidatoin with
well resistance. It is different, but still a square matrix, for
the stone column consolidation problem. As long as you know what you
are doing Ipsiw can be any appropriate square matrix.
a : PolyLine
Piewcewise linear function. e.g. for 1d consolidation surcharge
radial draiange term is D[kv(z)*D[u(Z,t), Z],Z] so `a` would be kv.
be et.
top_vs_time, bot_vs_time : list of PolyLine
Piecewise linear magnitude vs time for the top and bottom boundary.
Use ``None`` if there is no variation.
top_omega_phase, bot_omega_phase : list of 2 element tuples, optional
(omega, phase) for use in cos(omega * t + phase) * mag_vs_time
if omega_phase is None then mag_vs_time will not be multiplied by a
cosine. If any element of omega_phase is None then in that particular
loading combo, mag_vs_time will not be multiplied by a cosine.
dT : ``float``, optional
Time factor multiple for numerical convieniece. Default dT=1.0.
theta_zero_indexes : slice/list etc., optional
A slice object, list, etc that can be used for numpy fancy indexing.
Any specified index of the theta vector will be set to zero. This is
useful when using the spectral method with block matrices and the
loading term only refers to a subset of the equations. When using
block matrices m should be the same size as the block matrix.
Default theta_zero_indexes=None i.e. no elements of theta will be
set to zero.
Returns
-------
foft_Ipsiw_the: ndarray
Additional homgenising term of size (neig, len(t)).
Notes
-----
Assuming the loads are formulated as the product of separate time and depth
dependant functions as well as a cyclic component:
.. math:: \\sigma\\left({Z,t}\\right)=
\\sigma\\left({Z}\\right)
\\sigma\\left({t}\\right)
\\cos\\left(\\omega t + \\phi\\right)
the solution to consolidation equation using the spectral method has a
similar form to the following:
.. math:: u\\left(Z,t\\right)=
\\mathbf{\\phi} \\mathbf{v} \\mathbf{E}
\\left(\\mathbf{\\Gamma v}\\right)^{-1}
\\mathbf{\\theta}
When we consider non-zero boundary conditions, additional loading terms are
created when we sub in the following into the original governing equation.
.. math:: u\\left({Z,t}\\right)=
v\\left({Z,t}\\right) +
u_{top}\\left({t}\\right)\\left({1-Z}\\right) +
u_{bot}\\left({b}\\right)Z
As well as loading terms the process may produce an extra term to be
added to the final solution of the following form:
.. math:: \\mathbf{\\phi}
\\sigma\\left({t}\\right)
\\mathbf{\\psi}_{w}^{1}
\\mathbf{\\theta}_w
There are actually two of these terms one
for the top boundary condition and one for the bottom boundary condition.
This function calculates :math:`\\sigma\\left({t}\\right)\\mathbf{\\psi}_{w}^{-1}\\mathbf{\\theta}_w`
when substitutions are made in terms of the following form:
.. math:: \\frac{\\partial}{\\partial Z}
\\left(
{a\\left({Z}\\right)
\\frac{\\partial u\\left({Z,t}\\right)}{\\partial Z}}
\\right)
It is assumed that :math:`u_{top}\\left({t}\\right)` and
:math:`u_{bot}\\left({t}\\right)` are piecewise linear
in time with a cyclic component, and that multiple functions are
superposed. Also :math:`a\\left(Z\\right)`
is a piecewise linear function with respect to :math:`Z`
For this particular function the :math:`\\mathbf{\\theta}` vector for
each load is given by:
.. math:: \\mathbf{\\theta}_{i}=
\\int_{0}^1{
\\frac{\\partial}{\\partial Z}
\\left(
{a\\left({Z}\\right)
\\frac{\\partial \\sigma\\left({Z}\\right)}{\\partial Z}}
\\right)
f\\left({Z}\\right)
\\phi_i\\,dZ}
Where :math:`f\\left({Z}\\right)` is the appropriate z-dependent term
corresponding to either :math:`u_{top}` or :math:`u_{bot}` homogenisations.
The time dependent function evaluated at each time produces a matrix
that we will call :math:`\\mathbf{E}` (not to be confused with other E
matrices) which is given by:
.. math:: \\mathbf{E}_{j}=
{\\sigma\\left(t_j\\right)}
{\\cos\\left(\\omega t_j+\\textrm{phase}\\right)}
Note that the listed equations above are in terms of normalised depth Z,
with depth integrations between [0, 1]. However, IF YOU KNOW WHAT YOU
ARE DOING the integrations can be done using non-normalised depths.
The first z value in the piecewise definition a(z) must still be 0
however the end point for integration will be the final z value in the
definition of a(z). If you are doing this then your `m` values will
include the normalising Factor. e.g. m = [pi/2/H, 3*pi/2/H] and a(z) is
defined in two layers [0, z1], [z1, zend] as opposed to
m = [pi/2, 3*pi/2] and a(Z) is two layers [0, z1/H], [z1/H, zend/H].
"""
foft_Ipsiw_the = np.zeros((len(m), len(tvals)))
if not a is None:
if drn==1:
zdist = PolyLine(a.x1,a.x2, np.ones_like(a.x1), np.ones_like(a.x2))
#bot_vs_time=None
else:
zdist = PolyLine(a.x1,a.x2, a.x2[-1]-a.x1, a.x2[-1]-a.x2)
if not top_vs_time is None:
if top_omega_phase is None:
top_omega_phase = [None] * len(top_vs_time)
theta = integ.pdim1sin_D_aDb_linear(m, a, zdist)
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
for top_vs_t, om_ph in zip(top_vs_time, top_omega_phase):
E = pwise.pinterp_x_y(top_vs_t, tvals)
if not om_ph is None:
omega, phase = om_ph
E*= np.cos(omega * tvals + phase)
foft_Ipsiw_the += E[None, :]*np.dot(Ipsiw, theta)[:, None]
if not bot_vs_time is None:
if bot_omega_phase is None:
bot_omega_phase = [None] * len(bot_vs_time)
theta = integ.pdim1sin_D_aDb_linear(m, a, PolyLine(a.x1,a.x2,a.x1,a.x2))
if not theta_zero_indexes is None:
theta[theta_zero_indexes] = 0.0
for bot_vs_t, om_ph in zip(bot_vs_time, bot_omega_phase):
E = pwise.pinterp_x_y(bot_vs_t, tvals)
if not om_ph is None:
omega, phase = om_ph
E*= np.cos(omega * tvals + phase)
foft_Ipsiw_the += E[None, :]*np.dot(Ipsiw, theta)[:, None]
#theta is 1d array, Igamv is nieg by neig array, np.dot(Igamv, theta)
#and np.dot(theta, Igamv) will give differetn 1d arrays.
#Basically np.dot(Igamv, theta) gives us what we want i.e.
#theta was treated as a column array. The alternative
#np.dot(theta, Igamv) would have treated theta as a row vector.
return foft_Ipsiw_the
[docs]def dim1sin_E_Igamv_the_mvpl(m,
eigs,
tvals,
Igamv,
moving_loads,
# pseudo_k,
# mag_vs_time,
# omega_phase=None,
dT=1.0,
theta_zero_indexes=None,
implementation='vectorized'):
"""Calculate E and theta parts and assemble E_Igamv_the matrix
for loading terms of the form a(Z) * delta(Z-Zd)*mag(t) where mag is
piecewise linear in time multiplied by cos(omega * t + phase).
Make the E*inverse(gam*v)*theta part of solution
u(Z,t)=phi*v*E*inverse(gam*v)*theta for terms of the form
a(Z) * delta(Z-Zd)*mag(t).
The contribution of each `mag_vs_time`-`omega_phase` pairing and each zval
are superposed. The result is an array
of size (neig, len(tvals)). So each column is the are the column vector
E*inverse(gam*v)*theta calculated at each output time. This will allow
us later to do u(Z,t) = phi*v*E_Igamv_the.
Uses sin(m*Z) in the calculation of theta.
Parameters
----------
m : ``list`` of ``float``
Eigenvalues of BVP, the m in sin(m*Z). Generate with
geotecha.speccon.m_from_sin_mx.
eigs : 1d numpy.ndarray
List of eigenvalues of the spectral matrix i.e. Eigenvalues of the
square Igam_psi matrix.
tvals : 1d numpy.ndarray`
List of time values to evaluate E matrix at.
Igamv : ndarray
Speccon matrix. Igamv = inverse of [gam * v])
moving_loads : list of MovingPointLoads objects
List of loads to apply .
dT : ``float``, optional
Time factor multiple for numerical convieniece. Default dT=1.0.
theta_zero_indexes : slice/list etc., optional
A slice object, list, etc that can be used for numpy fancy indexing.
Any specified index of the theta vector will be set to zero. This is
useful when using the spectral method with block matrices and the
loading term only refers to a subset of the equations. When using
block matrices m should be the same size as the block matrix.
Default theta_zero_indexes=None i.e. no elements of theta will be
set to zero.
Returns
-------
E_Igamv_the : ndarray, dtype=complex
Loading matrix of size (neig, len(tvals)).
Notes
-----
returns a complex array!!!
Assuming the loads are formulated as the product of separate time and depth
dependant functions as well as a cyclic component:
"""
E_the = np.zeros((len(tvals), len(eigs), len(m)),dtype=complex)
#axes are (t,eig,theta), they will be transposed at the end of the function.
if not theta_zero_indexes is None:
avoid = range(len(m))[theta_zero_indexes]
else:
avoid = []
for mvpl in moving_loads:
plines, omega_phases = mvpl.convert_to_specbeam()
for mag_vs_t, omega_phase in zip(plines, omega_phases):
omega, phase = omega_phase
for i, mi in enumerate(m):
if i in avoid:
continue
E_the[:, :, i] += (
integ.pEload_sinlinear(mag_vs_t,
omega*mi, phase*mi,
eigs,
tvals, dT,
implementation=implementation))
# if not theta_zero_indexes is None:
# E_the[:, :, theta_zero_indexes] = 0.0
E_the *= Igamv[np.newaxis, :, :]
E_Igamv_the = E_the.sum(axis=-1).T
# if omega_phase is None:
# omega_phase = [None] * len(mag_vs_time)
#
# for z, k, mag_vs_t, om_ph in zip(zvals, pseudo_k, mag_vs_time, omega_phase):
# if mag_vs_t is None:
# continue
# theta = k * np.sin(z * m)
# if not theta_zero_indexes is None:
# theta[theta_zero_indexes] = 0.0
# if not om_ph is None:
# omega, phase = om_ph
# E = integ.pEload_coslinear(mag_vs_t, omega, phase, eigs, tvals, dT, implementation=implementation)
# else:
# E = integ.pEload_linear(mag_vs_t, eigs, tvals, dT, implementation=implementation)
# E_Igamv_the += (E*np.dot(Igamv, theta)).T
return E_Igamv_the
if __name__ == '__main__':
import nose
nose.runmodule(argv=['nose', '--verbosity=3', '--with-doctest'])
# nose.runmodule(argv=['nose', '--verbosity=3'])