Source code for geotecha.speccon.integrals_generate_code

# 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.
"""Use sympy to generate code for generating spectral method matrix subroutines"""

from __future__ import division, print_function

import sympy
import textwrap
import os

from geotecha.inputoutput.inputoutput import fcode_one_large_expr


[docs]def tw(text, indents=3, width=100, break_long_words=False): """Rough text wrapper for long sympy expressions 1st line will not be indented. Parameters ---------- text : str Text to wrap width : int optional Rough width of warpping. Default width=100. indents : int, optional Multiple of 4 spaces that will be used to indent each line. Default indents=3. break_long_words : True/False, optional Default break_long_words=False. Returns ------- out : str Multi-line string. """ subsequent_indent = " "*4*indents wrapper = textwrap.TextWrapper(width=width, subsequent_indent = subsequent_indent, break_long_words=break_long_words) lines = wrapper.wrap(str(text)) lines[0] = lines[0].strip() return os.linesep.join(lines)
[docs]def Eload_linear_implementations(): """Code generation for Integration of load(tau) * exp(dT * eig * (t-tau)) between [0, t], where load(tau) is piecewise linear. Performs integrations involving a piecewise linear load. A 2d array of dimensions A[len(tvals), len(eigs)] is produced where the 'i'th row of A contains the diagonal elements of the spectral 'E' matrix calculated for the time value tvals[i]. i.e. rows of this matrix will be assembled into the diagonal matrix 'E' elsewhere. Paste the resulting code (at least the loops) into `Eload_linear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. Needs to be compiled with f2py. Notes ----- This function produces a complex array Assuming the load are formulated as the product of separate time and depth dependant functions: .. math:: \\sigma\\left({Z,t}\\right)=\\sigma\\left({Z}\\right)\\sigma\\left({t}\\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} The matrix :math:`E` is a time dependent diagonal matrix due to time dependant loadings. The version of :math:`E` calculated here in `Eload_linear` is from loading terms in the governing equation that are NOT differentiated wrt :math:`t`. The diagonal elements of :math:`E` are given by: .. math:: \\mathbf{E}_{i,i}=\\int_{0}^t{{\\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 time dependant portion of the loading function. When the time dependant loading term :math:`\\sigma\\left(\\tau\\right)` is piecewise in time. The contribution of each load segment is found by: .. math:: \\mathbf{E}_{i,i}=\\int_{t_s}^{t_f}{{\\sigma\\left(\\tau\\right)}\\exp\\left({dT\\left(t-\\tau\\right)*\\lambda_i}\\right)\\,d\\tau} where .. math:: t_s = \\min\\left(t,t_{increment\\:start}\\right) .. math:: t_f = \\min\\left(t,t_{increment\\:end}\\right) (note that this function,`Eload_linear`, rather than use :math:`t_s` and :math:`t_f`, explicitly finds increments that the current time falls in, falls after, and falls before and treates each case on it's own.) Each :math:`t` value of interest requires a separate diagonal matrix :math:`E`. To use space more efficiently and to facilitate numpy broadcasting when using the results of the function, the diagonal elements of :math:`E` for each time value `t` value are stored in the rows of array :math:`A` returned by `Eload_linear`. Thus: .. math:: \\mathbf{A}=\\left(\\begin{matrix}E_{0,0}(t_0)&E_{1,1}(t_0)& \cdots & E_{neig-1,neig-1}(t_0)\\\ E_{0,0}(t_1)&E_{1,1}(t_1)& \\cdots & E_{neig-1,neig-1}(t_1)\\\ \\vdots&\\vdots&\\ddots&\\vdots \\\ E_{0,0}(t_m)&E_{1,1}(t_m)& \cdots & E_{neig-1,neig-1}(t_m)\\end{matrix}\\right) See Also -------- geotecha.speccon.integrals.Eload_linear : Resulting function. geotecha.speccon.integrals.pEload_linear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.eload_linear : Resulting fortran function. Eload_coslinear_implementations : Similar function with additional cosine term. EDload_linear_implementations : Similar function but the time dependent loading function is differentiated with respect to time. """ from sympy import exp sympy.var('t, tau, dT, eig') loadmag = sympy.tensor.IndexedBase('loadmag') loadtim = sympy.tensor.IndexedBase('loadtim') tvals = sympy.tensor.IndexedBase('tvals') eigs = sympy.tensor.IndexedBase('eigs') i = sympy.tensor.Idx('i') j = sympy.tensor.Idx('j') k = sympy.tensor.Idx('k') t0, t1, sig0, sig1 = sympy.symbols('t0, t1, sig0, sig1') load = sig0 + (sig1 - sig0)/(t1 - t0) * (tau - t0) x, x0, x1 = sympy.symbols('x, x0, x1') load = load.subs(tau, x/(dT*eig)+t) dx_dtau = dT * eig mp = [(x0, -dT * eig*(t-t0)), (x1, -dT * eig*(t-t1))] mp2 = [(sig0, loadmag[k]), (sig1, loadmag[k+1]), (t0, loadtim[k]), (t1, loadtim[k+1])] # mp = [(exp(-dT*eig*t)*exp(dT*eig*loadtim[k]), # exp(-dT*eig*(t-loadtim[k]))), # (exp(-dT*eig*t)*exp(dT*eig*loadtim[k+1]), # exp(-dT*eig*(t-loadtim[k+1])))] # the default output for the integrals will have expression s like # exp(-dT*eig*t)*exp(dT*eig*loadtim[k]). when dT*eig*loadtim[k] is large # the exponential may be so large as to cause an error. YOu may need to # manually alter the expression to exp(is large exp(-dT*eig*(t-loadtim[k])) # in which case the term in the exponential will always be negative and not # lead to any numerical blow up. # load = linear(tau, loadtim[k], loadmag[k], loadtim[k+1], loadmag[k+1]) # after_instant = (loadmag[k+1] - loadmag[k]) * exp(-dT * eig * (t - loadtim[k])) # mp does this automatically with subs # load = linear(tau, loadtim[k], loadmag[k], loadtim[k+1], loadmag[k+1]) within_constant = sig0 * exp(x) / dx_dtau within_constant = sympy.integrate(within_constant, (x, x0, 0),risch=False, conds='none') within_constant = within_constant.subs(mp) within_constant = within_constant.subs(mp2) after_constant = sig0 * exp(x) / dx_dtau after_constant = sympy.integrate(after_constant, (x, x0, x1), risch=False, conds='none') after_constant = after_constant.subs(mp) after_constant = after_constant.subs(mp2) within_ramp = load * exp(x) / dx_dtau within_ramp = sympy.integrate(within_ramp, (x, x0, 0), risch=False, conds='none') within_ramp = within_ramp.subs(mp) within_ramp = within_ramp.subs(mp2) after_ramp = load * exp(x) / dx_dtau after_ramp = sympy.integrate(after_ramp, (x, x0, x1), risch=False, conds='none') after_ramp = after_ramp.subs(mp) after_ramp = after_ramp.subs(mp2) text_python = """\ def Eload_linear(loadtim, loadmag, eigs, tvals, dT=1.0, implementation='vectorized'): loadtim = np.asarray(loadtim) loadmag = np.asarray(loadmag) eigs = np.asarray(eigs) tvals = np.asarray(tvals) if implementation == 'scalar': sin = np.sin cos = np.cos exp = np.exp A = np.zeros([len(tvals), len(eigs)], dtype=complex) (ramps_less_than_t, constants_less_than_t, steps_less_than_t, ramps_containing_t, constants_containing_t) = segment_containing_also_segments_less_than_xi(loadtim, loadmag, tvals, steps_or_equal_to = True) for i, t in enumerate(tvals): for k in constants_containing_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({0}) for k in constants_less_than_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({1}) for k in ramps_containing_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({2}) for k in ramps_less_than_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({3}) elif implementation == 'fortran': #note than all fortran subroutines are lowercase. if MUST_TRY_FORTRAN: from geotecha.speccon.ext_integrals import eload_linear as fn else: try: from geotecha.speccon.ext_integrals import eload_linear as fn except ImportError: fn = Eload_linear A = fn(loadtim, loadmag, eigs, tvals, dT) else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos exp = np.exp A = np.zeros([len(tvals), len(eigs)], dtype=complex) (ramps_less_than_t, constants_less_than_t, steps_less_than_t, ramps_containing_t, constants_containing_t) = segment_containing_also_segments_less_than_xi(loadtim, loadmag, tvals, steps_or_equal_to = True) eig = eigs[:, None] for i, t in enumerate(tvals): k = constants_containing_t[i] if len(k): A[i, :] += np.sum({0}, axis=1) k = constants_less_than_t[i] if len(k): A[i, :] += np.sum({1}, axis=1) k = ramps_containing_t[i] if len(k): A[i, :] += np.sum({2}, axis=1) k = ramps_less_than_t[i] if len(k): A[i, :] += np.sum({3}, axis=1) return A""" text_fortran = """\ SUBROUTINE eload_linear(loadtim, loadmag, eigs, tvals,& dT, a, neig, nload, nt) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nload INTEGER, intent(in) :: nt REAL(DP), intent(in), dimension(0:nload-1) :: loadtim REAL(DP), intent(in), dimension(0:nload-1) :: loadmag COMPLEX(DP), intent(in), dimension(0:neig-1) :: eigs REAL(DP), intent(in), dimension(0:nt-1) :: tvals REAL(DP), intent(in) :: dT COMPLEX(DP), intent(out), dimension(0:nt-1, 0:neig-1) :: a INTEGER :: i , j, k REAL(DP):: EPSILON a=0.0D0 EPSILON = 0.0000005D0 DO i = 0, nt-1 DO k = 0, nload-2 IF (tvals(i) < loadtim(k)) EXIT !t is before load step IF (tvals(i) >= loadtim(k + 1)) THEN !t is after the load step IF(ABS(loadtim(k) - loadtim(k + 1)) <= & (ABS(loadtim(k) + loadtim(k + 1))*EPSILON)) THEN !step load CONTINUE ELSEIF(ABS(loadmag(k) - loadmag(k + 1)) <= & (ABS(loadmag(k) + loadmag(k + 1))*EPSILON)) THEN !constant load DO j=0, neig-1 {0} END DO ELSE !ramp load DO j=0, neig-1 {1} END DO END IF ELSE !t is in the load step IF(ABS(loadmag(k) - loadmag(k + 1)) <= & (ABS(loadmag(k) + loadmag(k + 1))*EPSILON)) THEN !constant load DO j=0, neig-1 {2} END DO ELSE !ramp load DO j=0, neig-1 {3} END DO END IF END IF END DO END DO END SUBROUTINE """ fn = text_python.format( tw(within_constant, 6), tw(after_constant, 6), tw(within_ramp, 6), tw(after_ramp, 6)) mp3 = [(eig, eigs[j]), (t,tvals[i])] after_constant = after_constant.subs(mp3) after_ramp = after_ramp.subs(mp3) within_constant = within_constant.subs(mp3) within_ramp = within_ramp.subs(mp3) fn2 = text_fortran.format( fcode_one_large_expr(after_constant, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(after_ramp, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(within_constant, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(within_ramp, prepend='a(i, j) = a(i, j) + ')) return fn, fn2
[docs]def EDload_linear_implementations(): """Code generation for Integration of D[load(tau), tau] * exp(dT * eig * (t-tau)) between [0, t], where load(tau) is piecewise linear. Performs integrations involving a piecewise linear load. A 2d array of dimensions A[len(tvals), len(eigs)] is produced where the 'i'th row of A contains the diagonal elements of the spectral 'E' matrix calculated for the time value tvals[i]. i.e. rows of this matrix will be assembled into the diagonal matrix 'E' elsewhere. Paste the resulting code (at least the loops) into `EDload_linear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. needs to be compiled with f2py Notes ----- Assuming the load are formulated as the product of separate time and depth dependant functions: .. math:: \\sigma\\left({Z,t}\\right)=\\sigma\\left({Z}\\right)\\sigma\\left({t}\\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} The matrix :math:`E` is a time dependent diagonal matrix due to time dependant loadings. The version of :math:`E` calculated here in `EDload_linear` is from loading terms in the governing equation that are NOT differentiated wrt :math:`t`. The diagonal elements of :math:`E` are given by: .. math:: \\mathbf{E}_{i,i}=\\int_{0}^t{\\frac{d{\\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 time dependant portion of the loading function. When the time dependant loading term :math:`\\sigma\\left(\\tau\\right)` is piecewise in time. The contribution of each load segment is found by: .. math:: \\mathbf{E}_{i,i}=\\int_{t_s}^{t_f}{{\\sigma\\left(\\tau\\right)}\\exp\\left({dT\\left(t-\\tau\\right)*\\lambda_i}\\right)\\,d\\tau} where .. math:: t_s = \\min\\left(t,t_{increment\\:start}\\right) .. math:: t_f = \\min\\left(t,t_{increment\\:end}\\right) (note that this function,`EDload_linear`, rather than use :math:`t_s` and :math:`t_f`, explicitly finds increments that the current time falls in, falls after, and falls before and treates each case on it's own.) Each :math:`t` value of interest requires a separate diagonal matrix :math:`E`. To use space more efficiently and to facilitate numpy broadcasting when using the results of the function, the diagonal elements of :math:`E` for each time value `t` value are stored in the rows of array :math:`A` returned by `EDload_linear`. Thus: .. math:: \\mathbf{A}=\\left(\\begin{matrix}E_{0,0}(t_0)&E_{1,1}(t_0)& \cdots & E_{neig-1,neig-1}(t_0)\\\ E_{0,0}(t_1)&E_{1,1}(t_1)& \\cdots & E_{neig-1,neig-1}(t_1)\\\ \\vdots&\\vdots&\\ddots&\\vdots \\\ E_{0,0}(t_m)&E_{1,1}(t_m)& \cdots & E_{neig-1,neig-1}(t_m)\\end{matrix}\\right) See Also -------- geotecha.speccon.integrals.EDload_linear : Resulting function. geotecha.speccon.integrals.pEDload_linear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.edload_linear : Resulting fortran function. EDload_coslinear_implementations : Similar function with additional cosine term. Eload_linear_implementations : Similar function but the time dependent loading function is not differentiated with respect to time. """ from sympy import exp sympy.var('t, tau, dT, eig') loadmag = sympy.tensor.IndexedBase('loadmag') loadtim = sympy.tensor.IndexedBase('loadtim') tvals = sympy.tensor.IndexedBase('tvals') eigs = sympy.tensor.IndexedBase('eigs') i = sympy.tensor.Idx('i') j = sympy.tensor.Idx('j') k = sympy.tensor.Idx('k') t0, t1, sig0, sig1 = sympy.symbols('t0, t1, sig0, sig1') load = sig0 + (sig1 - sig0)/(t1 - t0) * (tau - t0) x, x0, x1 = sympy.symbols('x, x0, x1') load = load.subs(tau, x/(dT*eig)+t) dx_dtau = dT * eig mp = [(x0, -dT * eig*(t-t0)), (x1, -dT * eig*(t-t1))] # the default output for the integrals will have expression s like # exp(-dT*eig*t)*exp(dT*eig*loadtim[k]). when dT*eig*loadtim[k] is large # the exponential may be so large as to cause an error. YOu may need to # manually alter the expression to exp(is large exp(-dT*eig*(t-loadtim[k])) # in which case the term in the exponential will always be negative and not # lead to any numerical blow up. mp2 = [(sig0, loadmag[k]), (sig1, loadmag[k + 1]), (t0, loadtim[k]), (t1, loadtim[k + 1])] Dload = sympy.diff(load, x) * dx_dtau after_instant = (sig1 - sig0) * exp(x0) after_instant = after_instant.subs(mp) after_instant = after_instant.subs(mp2) within_ramp = Dload * exp(x) / dx_dtau within_ramp = sympy.integrate(within_ramp, (x, x0, 0), risch=False, conds='none') within_ramp = within_ramp.subs(mp) within_ramp = within_ramp.subs(mp2) after_ramp = Dload * exp(x) / dx_dtau after_ramp = sympy.integrate(after_ramp, (x, x0, x1), risch=False, conds='none') after_ramp = after_ramp.subs(mp) after_ramp = after_ramp.subs(mp2) text_python = """\ def EDload_linear(loadtim, loadmag, eigs, tvals, dT=1.0, implementation='vectorized'): loadtim = np.asarray(loadtim) loadmag = np.asarray(loadmag) eigs = np.asarray(eigs) tvals = np.asarray(tvals) if implementation == 'scalar': sin = math.sin cos = math.cos exp = math.exp A = np.zeros([len(tvals), len(eigs)]) (ramps_less_than_t, constants_less_than_t, steps_less_than_t, ramps_containing_t, constants_containing_t) = segment_containing_also_segments_less_than_xi(loadtim, loadmag, tvals, steps_or_equal_to = True) for i, t in enumerate(tvals): for k in steps_less_than_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({0}) for k in ramps_containing_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({1}) for k in ramps_less_than_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({2}) elif implementation == 'fortran': #note than all fortran subroutines are lowercase. if MUST_TRY_FORTRAN: from geotecha.speccon.ext_integrals import edload_linear as fn else: try: from geotecha.speccon.ext_integrals import edload_linear as fn except ImportError: fn = EDload_linear A = fn(loadtim, loadmag, eigs, tvals, dT) else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos exp = np.exp A = np.zeros([len(tvals), len(eigs)]) (ramps_less_than_t, constants_less_than_t, steps_less_than_t, ramps_containing_t, constants_containing_t) = segment_containing_also_segments_less_than_xi(loadtim, loadmag, tvals, steps_or_equal_to = True) eig = eigs[:, None] for i, t in enumerate(tvals): k = steps_less_than_t[i] if len(k): A[i, :] += np.sum({0}, axis=1) k = ramps_containing_t[i] if len(k): A[i, :] += np.sum({1}, axis=1) k = ramps_less_than_t[i] if len(k): A[i, :] += np.sum({2}, axis=1) return A""" text_fortran = """\ SUBROUTINE edload_linear(loadtim, loadmag, eigs, tvals,& dT, a, neig, nload, nt) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nload INTEGER, intent(in) :: nt REAL(DP), intent(in), dimension(0:nload-1) :: loadtim REAL(DP), intent(in), dimension(0:nload-1) :: loadmag REAL(DP), intent(in), dimension(0:neig-1) :: eigs REAL(DP), intent(in), dimension(0:nt-1) :: tvals REAL(DP), intent(in) :: dT REAL(DP), intent(out), dimension(0:nt-1, 0:neig-1) :: a INTEGER :: i , j, k REAL(DP):: EPSILON a=0.0D0 EPSILON = 0.0000005D0 DO i = 0, nt-1 DO k = 0, nload-2 IF (tvals(i) < loadtim(k)) EXIT !t is before load step IF (tvals(i) >= loadtim(k + 1)) THEN !t is after the load step IF(ABS(loadtim(k) - loadtim(k + 1)) <= & (ABS(loadtim(k) + loadtim(k + 1))*EPSILON)) THEN !step load DO j=0, neig-1 {0} END DO ELSEIF(ABS(loadmag(k) - loadmag(k + 1)) <= & (ABS(loadmag(k) + loadmag(k + 1))*EPSILON)) THEN !constant load CONTINUE ELSE !ramp load DO j=0, neig-1 {1} END DO END IF ELSE !t is in the load step IF(ABS(loadmag(k) - loadmag(k + 1)) <= & (ABS(loadmag(k) + loadmag(k + 1))*EPSILON)) THEN !constant load CONTINUE ELSE !ramp load DO j=0, neig-1 {2} END DO END IF END IF END DO END DO END SUBROUTINE """ fn = text_python.format( tw(after_instant, 6), tw(within_ramp, 6), tw(after_ramp, 6)) mp3 = [(eig, eigs[j]), (t, tvals[i])] after_instant = after_instant.subs(mp3) after_ramp = after_ramp.subs(mp3) within_ramp = within_ramp.subs(mp3) fn2 = text_fortran.format( fcode_one_large_expr(after_instant, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(after_ramp, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(within_ramp, prepend='a(i, j) = a(i, j) + ')) return fn, fn2
[docs]def Eload_coslinear_implementations(): """Code generation for Integration of cos(omega*tau+phase)*load(tau) * exp(dT * eig * (t-tau)) between [0, t], where load(tau) is piecewise linear. Performs integrations involving a piecewise linear load. A 2d array of dimensions A[len(tvals), len(eigs)] is produced where the 'i'th row of A contains the diagonal elements of the spectral 'E' matrix calculated for the time value tvals[i]. i.e. rows of this matrix will be assembled into the diagonal matrix 'E' elsewhere. Paste the resulting code (at least the loops) into `Eload_coslinear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. needs to be compiled with f2py Notes ----- Assuming the load are formulated as the product of separate time and depth dependant functions: .. math:: \\sigma\\left({Z,t}\\right)=\\sigma\\left({Z}\\right)\\sigma\\left({t}\\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} The matrix :math:`E` is a time dependent diagonal matrix due to time dependant loadings. The version of :math:`E` calculated here in `Eload_coslinear` is from loading terms in the governing equation that are NOT differentiated wrt :math:`t`. The diagonal elements of :math:`E` are given by: .. math:: \\mathbf{E}_{i,i}=\\int_{0}^t{{\\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 time dependant portion of the loading function. When the time dependant loading term :math:`\\sigma\\left(\\tau\\right)` is piecewise in time. The contribution of each load segment is found by: .. math:: \\mathbf{E}_{i,i}=\\int_{t_s}^{t_f}{{\\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:: t_s = \\min\\left(t,t_{increment\\:start}\\right) .. math:: t_f = \\min\\left(t,t_{increment\\:end}\\right) (note that this function,`Eload_coslinear`, rather than use :math:`t_s` and :math:`t_f`, explicitly finds increments that the current time falls in, falls after, and falls before and treates each case on it's own.) Each :math:`t` value of interest requires a separate diagonal matrix :math:`E`. To use space more efficiently and to facilitate numpy broadcasting when using the results of the function, the diagonal elements of :math:`E` for each time value `t` value are stored in the rows of array :math:`A` returned by `Eload_coslinear`. Thus: .. math:: \\mathbf{A}=\\left(\\begin{matrix}E_{0,0}(t_0)&E_{1,1}(t_0)& \cdots & E_{neig-1,neig-1}(t_0)\\\ E_{0,0}(t_1)&E_{1,1}(t_1)& \\cdots & E_{neig-1,neig-1}(t_1)\\\ \\vdots&\\vdots&\\ddots&\\vdots \\\ E_{0,0}(t_m)&E_{1,1}(t_m)& \cdots & E_{neig-1,neig-1}(t_m)\\end{matrix}\\right) See Also -------- geotecha.speccon.integrals.Eload_coslinear : Resulting function. geotecha.speccon.integrals.pEload_coslinear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.eload_coslinear : Resulting fortran function. Eload_linear_implementations : Similar function with no cosine term. EDload_coslinear_implementations : Similar function but the time dependent loading function is differentiated with respect to time. """ from sympy import exp sympy.var('t, tau, dT, eig, omega, phase') loadmag = sympy.tensor.IndexedBase('loadmag') loadtim = sympy.tensor.IndexedBase('loadtim') tvals = sympy.tensor.IndexedBase('tvals') eigs = sympy.tensor.IndexedBase('eigs') i = sympy.tensor.Idx('i') j = sympy.tensor.Idx('j') k = sympy.tensor.Idx('k') t0, t1, sig0, sig1 = sympy.symbols('t0, t1, sig0, sig1') x, x0, x1 = sympy.symbols('x, x0, x1') A, B = sympy.symbols('A, B') # load1 = sympy.cos(omega * tau + phase) load1 = sympy.cos(A * x + B) load2 = sig0 + (sig1 - sig0)/(t1 - t0) * (tau - t0) load1 = load1.subs(tau, x/(dT*eig)+t) load2 = load2.subs(tau, x/(dT*eig)+t) dx_dtau = dT * eig mp = [(x0, -dT * eig*(t-t0)), (x1, -dT * eig*(t-t1)), (A, omega / (dT*eig)), (B, omega * t + phase)] mp2 = [(sig0, loadmag[k]), (sig1, loadmag[k+1]), (t0, loadtim[k]), (t1, loadtim[k+1])] # mp = [(exp(-dT*eig*t)*exp(dT*eig*loadtim[k]), # exp(-dT*eig*(t-loadtim[k]))), # (exp(-dT*eig*t)*exp(dT*eig*loadtim[k+1]), # exp(-dT*eig*(t-loadtim[k+1])))] # the default output for the integrals will have expression s like # exp(-dT*eig*t)*exp(dT*eig*loadtim[k]). when dT*eig*loadtim[k] is large # the exponential may be so large as to cause an error. YOu may need to # manually alter the expression to exp(is large exp(-dT*eig*(t-loadtim[k])) # in which case the term in the exponential will always be negative and not # lead to any numerical blow up. # load = linear(tau, loadtim[k], loadmag[k], loadtim[k+1], loadmag[k+1]) # after_instant = (loadmag[k+1] - loadmag[k]) * exp(-dT * eig * (t - loadtim[k])) # mp does this automatically with subs # load = linear(tau, loadtim[k], loadmag[k], loadtim[k+1], loadmag[k+1]) within_constant = sig0 * load1 * exp(x) / dx_dtau within_constant = sympy.integrate(within_constant, (x, x0, 0),risch=False, conds='none') within_constant = within_constant.subs(mp) within_constant = within_constant.subs(mp2) after_constant = sig0 * load1 * exp(x) / dx_dtau after_constant = sympy.integrate(after_constant, (x, x0, x1), risch=False, conds='none') after_constant = after_constant.subs(mp) after_constant = after_constant.subs(mp2) within_ramp = load1 * load2 * exp(x) / dx_dtau within_ramp = sympy.integrate(within_ramp, (x, x0, 0), risch=False, conds='none') within_ramp = within_ramp.subs(mp) within_ramp = within_ramp.subs(mp2) after_ramp = load1 * load2 * exp(x) / dx_dtau after_ramp = sympy.integrate(after_ramp, (x, x0, x1), risch=False, conds='none') after_ramp = after_ramp.subs(mp) after_ramp = after_ramp.subs(mp2) text_python = """\ def Eload_coslinear(loadtim, loadmag, omega, phase, eigs, tvals, dT=1.0, implementation='vectorized'): loadtim = np.asarray(loadtim) loadmag = np.asarray(loadmag) eigs = np.asarray(eigs) tvals = np.asarray(tvals) if implementation == 'scalar': sin = math.sin cos = math.cos exp = math.exp A = np.zeros([len(tvals), len(eigs)]) (ramps_less_than_t, constants_less_than_t, steps_less_than_t, ramps_containing_t, constants_containing_t) = segment_containing_also_segments_less_than_xi(loadtim, loadmag, tvals, steps_or_equal_to = True) for i, t in enumerate(tvals): for k in constants_containing_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({0}) for k in constants_less_than_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({1}) for k in ramps_containing_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({2}) for k in ramps_less_than_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({3}) elif implementation == 'fortran': #note than all fortran subroutines are lowercase. if MUST_TRY_FORTRAN: from geotecha.speccon.ext_integrals import eload_coslinear as fn else: try: from geotecha.speccon.ext_integrals import eload_coslinear as fn except ImportError: fn = Eload_coslinear A = fn(loadtim, loadmag, omega, phase, eigs, tvals, dT) else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos exp = np.exp A = np.zeros([len(tvals), len(eigs)]) (ramps_less_than_t, constants_less_than_t, steps_less_than_t, ramps_containing_t, constants_containing_t) = segment_containing_also_segments_less_than_xi(loadtim, loadmag, tvals, steps_or_equal_to = True) eig = eigs[:, None] for i, t in enumerate(tvals): k = constants_containing_t[i] if len(k): A[i, :] += np.sum({0}, axis=1) k = constants_less_than_t[i] if len(k): A[i, :] += np.sum({1}, axis=1) k = ramps_containing_t[i] if len(k): A[i, :] += np.sum({2}, axis=1) k = ramps_less_than_t[i] if len(k): A[i, :] += np.sum({3}, axis=1) return A""" text_fortran = """\ SUBROUTINE eload_coslinear(loadtim, loadmag, omega, phase, & eigs, tvals, dT, a, neig, nload, nt) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nload INTEGER, intent(in) :: nt REAL(DP), intent(in), dimension(0:nload-1) :: loadtim REAL(DP), intent(in), dimension(0:nload-1) :: loadmag REAL(DP), intent(in), dimension(0:neig-1) :: eigs REAL(DP), intent(in), dimension(0:nt-1) :: tvals REAL(DP), intent(in) :: dT REAL(DP), intent(in) :: omega REAL(DP), intent(in) :: phase REAL(DP), intent(out), dimension(0:nt-1, 0:neig-1) :: a INTEGER :: i , j, k REAL(DP):: EPSILON a=0.0D0 EPSILON = 0.0000005D0 DO i = 0, nt-1 DO k = 0, nload-2 IF (tvals(i) < loadtim(k)) EXIT !t is before load step IF (tvals(i) >= loadtim(k + 1)) THEN !t is after the load step IF(ABS(loadtim(k) - loadtim(k + 1)) <= & (ABS(loadtim(k) + loadtim(k + 1))*EPSILON)) THEN !step load CONTINUE ELSEIF(ABS(loadmag(k) - loadmag(k + 1)) <= & (ABS(loadmag(k) + loadmag(k + 1))*EPSILON)) THEN !constant load DO j=0, neig-1 {0} END DO ELSE !ramp load DO j=0, neig-1 {1} END DO END IF ELSE !t is in the load step IF(ABS(loadmag(k) - loadmag(k + 1)) <= & (ABS(loadmag(k) + loadmag(k + 1))*EPSILON)) THEN !constant load DO j=0, neig-1 {2} END DO ELSE !ramp load DO j=0, neig-1 {3} END DO END IF END IF END DO END DO END SUBROUTINE """ fn = text_python.format( tw(within_constant, 6), tw(after_constant, 6), tw(within_ramp, 6), tw(after_ramp, 6)) mp3 = [(eig, eigs[j]), (t,tvals[i])] after_constant = after_constant.subs(mp3) after_ramp = after_ramp.subs(mp3) within_constant = within_constant.subs(mp3) within_ramp = within_ramp.subs(mp3) fn2 = text_fortran.format( fcode_one_large_expr(after_constant, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(after_ramp, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(within_constant, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(within_ramp, prepend='a(i, j) = a(i, j) + ')) return fn, fn2
[docs]def EDload_coslinear_implementations(): """Code generation for Integration of D[cos(omega*tau+phase)*load(tau), tau] * exp(dT * eig * (t-tau)) between [0, t], where load(tau) is piecewise linear. Performs integrations involving a piecewise linear load. A 2d array of dimensions A[len(tvals), len(eigs)] is produced where the 'i'th row of A contains the diagonal elements of the spectral 'E' matrix calculated for the time value tvals[i]. i.e. rows of this matrix will be assembled into the diagonal matrix 'E' elsewhere. Paste the resulting code (at least the loops) into `EDload_coslinear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. Needs to be compiled with f2py. Notes ----- Assuming the load are formulated as the product of separate time and depth dependant functions: .. math:: \\sigma\\left({Z,t}\\right)=\\sigma\\left({Z}\\right)\\sigma\\left({t}\\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} The matrix :math:`E` is a time dependent diagonal matrix due to time dependant loadings. The version of :math:`E` calculated here in `EDload_coslinear` is from loading terms in the governing equation that are NOT differentiated wrt :math:`t`. The diagonal elements of :math:`E` are given by: .. math:: \\mathbf{E}_{i,i}=\\int_{0}^t{\\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 time dependant portion of the loading function. When the time dependant loading term :math:`\\sigma\\left(\\tau\\right)` is piecewise in time. The contribution of each load segment is found by: .. math:: \\mathbf{E}_{i,i}=\\int_{t_s}^{t_f}{\\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:: t_s = \\min\\left(t,t_{increment\\:start}\\right) .. math:: t_f = \\min\\left(t,t_{increment\\:end}\\right) (note that this function,`EDload_coslinear`, rather than use :math:`t_s` and :math:`t_f`, explicitly finds increments that the current time falls in, falls after, and falls before and treates each case on it's own.) Each :math:`t` value of interest requires a separate diagonal matrix :math:`E`. To use space more efficiently and to facilitate numpy broadcasting when using the results of the function, the diagonal elements of :math:`E` for each time value `t` value are stored in the rows of array :math:`A` returned by `EDload_coslinear`. Thus: .. math:: \\mathbf{A}=\\left(\\begin{matrix}E_{0,0}(t_0)&E_{1,1}(t_0)& \cdots & E_{neig-1,neig-1}(t_0)\\\ E_{0,0}(t_1)&E_{1,1}(t_1)& \\cdots & E_{neig-1,neig-1}(t_1)\\\ \\vdots&\\vdots&\\ddots&\\vdots \\\ E_{0,0}(t_m)&E_{1,1}(t_m)& \cdots & E_{neig-1,neig-1}(t_m)\\end{matrix}\\right) See Also -------- geotecha.speccon.integrals.EDload_coslinear : Resulting function. geotecha.speccon.integrals.pEDload_coslinear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.edload_coslinear : Resulting fortran function. EDload_linear_implementations : Similar function with no cosine term. Eload_coslinear_implementations : Similar function but the time dependent loading function is not differentiated w.r.t. time. """ from sympy import exp sympy.var('t, tau, dT, eig, omega, phase') loadmag = sympy.tensor.IndexedBase('loadmag') loadtim = sympy.tensor.IndexedBase('loadtim') tvals = sympy.tensor.IndexedBase('tvals') eigs = sympy.tensor.IndexedBase('eigs') i = sympy.tensor.Idx('i') j = sympy.tensor.Idx('j') k = sympy.tensor.Idx('k') t0, t1, sig0, sig1 = sympy.symbols('t0, t1, sig0, sig1') x, x0, x1 = sympy.symbols('x, x0, x1') A, B = sympy.symbols('A, B') # load1 = sympy.cos(omega * tau + phase) load1 = sympy.cos(A * x + B) load2 = sig0 + (sig1 - sig0)/(t1 - t0) * (tau - t0) load2 = load2.subs(tau, x/(dT*eig)+t) dx_dtau = dT * eig mp = [(x0, -dT * eig*(t-t0)), (x1, -dT * eig*(t-t1)), (A, omega / (dT*eig)), (B, omega * t + phase)] mp2 = [(sig0, loadmag[k]), (sig1, loadmag[k+1]), (t0, loadtim[k]), (t1, loadtim[k+1])] # the default output for the integrals will have expression s like # exp(-dT*eig*t)*exp(dT*eig*loadtim[k]). when dT*eig*loadtim[k] is large # the exponential may be so large as to cause an error. YOu may need to # manually alter the expression to exp(is large exp(-dT*eig*(t-loadtim[k])) # in which case the term in the exponential will always be negative and not # lead to any numerical blow up. mp2 = [(sig0, loadmag[k]), (sig1, loadmag[k + 1]), (t0, loadtim[k]), (t1, loadtim[k + 1])] Dload = sympy.diff(load1 * load2, x) * dx_dtau after_instant = (sig1 - sig0) * sympy.cos(omega * t0 + phase)*exp(-dT * eig * (t - t0)) after_instant = after_instant.subs(mp) after_instant = after_instant.subs(mp2) within_constant = sig0 * sympy.diff(load1, x) * exp(x) #dx_dtau cancels from diff and substitution within_constant = sympy.integrate(within_constant, (x, x0, 0), risch=False, conds='none') within_constant = within_constant.subs(mp) within_constant = within_constant.subs(mp2) after_constant = sig0 * sympy.diff(load1, x) * exp(x) #dx_dtau cancels from diff and substitution after_constant = sympy.integrate(after_constant, (x, x0, x1), risch=False, conds='none') after_constant = after_constant.subs(mp) after_constant = after_constant.subs(mp2) within_ramp = Dload * exp(x) / dx_dtau within_ramp = sympy.integrate(within_ramp, (x, x0, 0), risch=False, conds='none') within_ramp = within_ramp.subs(mp) within_ramp = within_ramp.subs(mp2) after_ramp = Dload * exp(x) / dx_dtau after_ramp = sympy.integrate(after_ramp, (x, x0, x1), risch=False, conds='none') after_ramp = after_ramp.subs(mp) after_ramp = after_ramp.subs(mp2) text_python = """\ def EDload_coslinear(loadtim, loadmag, omega, phase,eigs, tvals, dT=1.0, implementation='vectorized'): loadtim = np.asarray(loadtim) loadmag = np.asarray(loadmag) eigs = np.asarray(eigs) tvals = np.asarray(tvals) if implementation == 'scalar': sin = math.sin cos = math.cos exp = math.exp A = np.zeros([len(tvals), len(eigs)]) (ramps_less_than_t, constants_less_than_t, steps_less_than_t, ramps_containing_t, constants_containing_t) = segment_containing_also_segments_less_than_xi(loadtim, loadmag, tvals, steps_or_equal_to = True) for i, t in enumerate(tvals): for k in steps_less_than_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({0}) for k in ramps_containing_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({1}) for k in ramps_less_than_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({2}) for k in constants_containing_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({3}) for k in constants_less_than_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({4}) elif implementation == 'fortran': #note than all fortran subroutines are lowercase. if MUST_TRY_FORTRAN: from geotecha.speccon.ext_integrals import edload_coslinear as fn else: try: from geotecha.speccon.ext_integrals import edload_coslinear as fn except ImportError: fn = EDload_coslinear A = fn(loadtim, loadmag, omega, phase, eigs, tvals, dT) else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos exp = np.exp A = np.zeros([len(tvals), len(eigs)]) (ramps_less_than_t, constants_less_than_t, steps_less_than_t, ramps_containing_t, constants_containing_t) = segment_containing_also_segments_less_than_xi(loadtim, loadmag, tvals, steps_or_equal_to = True) eig = eigs[:, None] for i, t in enumerate(tvals): k = steps_less_than_t[i] if len(k): A[i, :] += np.sum({0}, axis=1) k = ramps_containing_t[i] if len(k): A[i, :] += np.sum({1}, axis=1) k = ramps_less_than_t[i] if len(k): A[i, :] += np.sum({2}, axis=1) k = constants_containing_t[i] if len(k): A[i,:] += np.sum({3}, axis=1) k = constants_less_than_t[i] if len(k): A[i,:] += np.sum({4}, axis=1) return A""" text_fortran = """\ SUBROUTINE edload_coslinear(loadtim, loadmag, omega, phase,& eigs, tvals, dT, a, neig, nload, nt) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nload INTEGER, intent(in) :: nt REAL(DP), intent(in), dimension(0:nload-1) :: loadtim REAL(DP), intent(in), dimension(0:nload-1) :: loadmag REAL(DP), intent(in), dimension(0:neig-1) :: eigs REAL(DP), intent(in), dimension(0:nt-1) :: tvals REAL(DP), intent(in) :: dT REAL(DP), intent(in) :: omega REAL(DP), intent(in) :: phase REAL(DP), intent(out), dimension(0:nt-1, 0:neig-1) :: a INTEGER :: i , j, k REAL(DP):: EPSILON a=0.0D0 EPSILON = 0.0000005D0 DO i = 0, nt-1 DO k = 0, nload-2 IF (tvals(i) < loadtim(k)) EXIT !t is before load step IF (tvals(i) >= loadtim(k + 1)) THEN !t is after the load step IF(ABS(loadtim(k) - loadtim(k + 1)) <= & (ABS(loadtim(k) + loadtim(k + 1))*EPSILON)) THEN !step load DO j=0, neig-1 {0} END DO ELSEIF(ABS(loadmag(k) - loadmag(k + 1)) <= & (ABS(loadmag(k) + loadmag(k + 1))*EPSILON)) THEN !constant load DO j=0, neig-1 {1} END DO ELSE !ramp load DO j=0, neig-1 {2} END DO END IF ELSE !t is in the load step IF(ABS(loadmag(k) - loadmag(k + 1)) <= & (ABS(loadmag(k) + loadmag(k + 1))*EPSILON)) THEN !constant load DO j=0, neig-1 {3} END DO ELSE !ramp load DO j=0, neig-1 {4} END DO END IF END IF END DO END DO END SUBROUTINE """ fn = text_python.format( tw(after_instant, 6), tw(within_ramp, 6), tw(after_ramp, 6), tw(within_constant, 6), tw(after_constant, 6)) mp3 = [(eig, eigs[j]), (t, tvals[i])] after_instant = after_instant.subs(mp3) after_ramp = after_ramp.subs(mp3) within_ramp = within_ramp.subs(mp3) after_constant = after_constant.subs(mp3) within_constant = within_constant.subs(mp3) fn2 = text_fortran.format( fcode_one_large_expr(after_instant, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(after_constant, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(after_ramp, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(within_constant, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(within_ramp, prepend='a(i, j) = a(i, j) + ')) return fn, fn2
[docs]def Eload_sinlinear_implementations(): """Code generation for Integration of sin(omega*tau+phase)*load(tau) * exp(dT * eig * (t-tau)) between [0, t], where load(tau) is piecewise linear. Performs integrations involving a piecewise linear load. A 2d array of dimensions A[len(tvals), len(eigs)] is produced where the 'i'th row of A contains the diagonal elements of the spectral 'E' matrix calculated for the time value tvals[i]. i.e. rows of this matrix will be assembled into the diagonal matrix 'E' elsewhere. Paste the resulting code (at least the loops) into `Eload_sinlinear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. needs to be compiled with f2py Notes ----- Note this will make complex arrays a complex array!!! Assuming the load are formulated as the product of separate time and depth dependant functions: .. math:: \\sigma\\left({Z,t}\\right)=\\sigma\\left({Z}\\right)\\sigma\\left({t}\\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} The matrix :math:`E` is a time dependent diagonal matrix due to time dependant loadings. The version of :math:`E` calculated here in `Eload_sinlinear` is from loading terms in the governing equation that are NOT differentiated wrt :math:`t`. The diagonal elements of :math:`E` are given by: .. math:: \\mathbf{E}_{i,i}=\\int_{0}^t{{\\sin\\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 time dependant portion of the loading function. When the time dependant loading term :math:`\\sigma\\left(\\tau\\right)` is piecewise in time. The contribution of each load segment is found by: .. math:: \\mathbf{E}_{i,i}=\\int_{t_s}^{t_f}{{\\sin\\left(\\omega\\tau+\\textrm{phase}\\right)}{\\sigma\\left(\\tau\\right)}\\exp\\left({dT\\left(t-\\tau\\right)*\\lambda_i}\\right)\\,d\\tau} where .. math:: t_s = \\min\\left(t,t_{increment\\:start}\\right) .. math:: t_f = \\min\\left(t,t_{increment\\:end}\\right) (note that this function,`Eload_sinlinear`, rather than use :math:`t_s` and :math:`t_f`, explicitly finds increments that the current time falls in, falls after, and falls before and treates each case on it's own.) Each :math:`t` value of interest requires a separate diagonal matrix :math:`E`. To use space more efficiently and to facilitate numpy broadcasting when using the results of the function, the diagonal elements of :math:`E` for each time value `t` value are stored in the rows of array :math:`A` returned by `Eload_sinlinear`. Thus: .. math:: \\mathbf{A}=\\left(\\begin{matrix}E_{0,0}(t_0)&E_{1,1}(t_0)& \cdots & E_{neig-1,neig-1}(t_0)\\\ E_{0,0}(t_1)&E_{1,1}(t_1)& \\cdots & E_{neig-1,neig-1}(t_1)\\\ \\vdots&\\vdots&\\ddots&\\vdots \\\ E_{0,0}(t_m)&E_{1,1}(t_m)& \cdots & E_{neig-1,neig-1}(t_m)\\end{matrix}\\right) See Also -------- geotecha.speccon.integrals.Eload_sinlinear : Resulting function. geotecha.speccon.integrals.pEload_sinlinear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.eload_sinlinear : Resulting fortran function. Eload_linear_implementations : Similar function with no sine term. Eload_coslinear_implementations : Similar function but with cosine term. """ from sympy import exp sympy.var('t, tau, dT, eig, omega, phase') loadmag = sympy.tensor.IndexedBase('loadmag') loadtim = sympy.tensor.IndexedBase('loadtim') tvals = sympy.tensor.IndexedBase('tvals') eigs = sympy.tensor.IndexedBase('eigs') i = sympy.tensor.Idx('i') j = sympy.tensor.Idx('j') k = sympy.tensor.Idx('k') t0, t1, sig0, sig1 = sympy.symbols('t0, t1, sig0, sig1') x, x0, x1 = sympy.symbols('x, x0, x1') A, B = sympy.symbols('A, B') # load1 = sympy.sin(omega * tau + phase) load1 = sympy.sin(A * x + B) load2 = sig0 + (sig1 - sig0)/(t1 - t0) * (tau - t0) load1 = load1.subs(tau, x/(dT*eig)+t) load2 = load2.subs(tau, x/(dT*eig)+t) dx_dtau = dT * eig mp = [(x0, -dT * eig*(t-t0)), (x1, -dT * eig*(t-t1)), (A, omega / (dT*eig)), (B, omega * t + phase)] mp2 = [(sig0, loadmag[k]), (sig1, loadmag[k+1]), (t0, loadtim[k]), (t1, loadtim[k+1])] # mp = [(exp(-dT*eig*t)*exp(dT*eig*loadtim[k]), # exp(-dT*eig*(t-loadtim[k]))), # (exp(-dT*eig*t)*exp(dT*eig*loadtim[k+1]), # exp(-dT*eig*(t-loadtim[k+1])))] # the default output for the integrals will have expression s like # exp(-dT*eig*t)*exp(dT*eig*loadtim[k]). when dT*eig*loadtim[k] is large # the exponential may be so large as to cause an error. YOu may need to # manually alter the expression to exp(is large exp(-dT*eig*(t-loadtim[k])) # in which case the term in the exponential will always be negative and not # lead to any numerical blow up. # load = linear(tau, loadtim[k], loadmag[k], loadtim[k+1], loadmag[k+1]) # after_instant = (loadmag[k+1] - loadmag[k]) * exp(-dT * eig * (t - loadtim[k])) # mp does this automatically with subs # load = linear(tau, loadtim[k], loadmag[k], loadtim[k+1], loadmag[k+1]) within_constant = sig0 * load1 * exp(x) / dx_dtau within_constant = sympy.integrate(within_constant, (x, x0, 0),risch=False, conds='none') within_constant = within_constant.subs(mp) within_constant = within_constant.subs(mp2) after_constant = sig0 * load1 * exp(x) / dx_dtau after_constant = sympy.integrate(after_constant, (x, x0, x1), risch=False, conds='none') after_constant = after_constant.subs(mp) after_constant = after_constant.subs(mp2) within_ramp = load1 * load2 * exp(x) / dx_dtau within_ramp = sympy.integrate(within_ramp, (x, x0, 0), risch=False, conds='none') within_ramp = within_ramp.subs(mp) within_ramp = within_ramp.subs(mp2) after_ramp = load1 * load2 * exp(x) / dx_dtau after_ramp = sympy.integrate(after_ramp, (x, x0, x1), risch=False, conds='none') after_ramp = after_ramp.subs(mp) after_ramp = after_ramp.subs(mp2) text_python = """\ def Eload_sinlinear(loadtim, loadmag, omega, phase, eigs, tvals, dT=1.0, implementation='vectorized'): loadtim = np.asarray(loadtim) loadmag = np.asarray(loadmag) eigs = np.asarray(eigs) tvals = np.asarray(tvals) if implementation == 'scalar': sin = np.sin cos = np.cos exp = np.exp #note that math module doesn't like complex numbers A = np.zeros([len(tvals), len(eigs)], dtype=complex) (ramps_less_than_t, constants_less_than_t, steps_less_than_t, ramps_containing_t, constants_containing_t) = segment_containing_also_segments_less_than_xi(loadtim, loadmag, tvals, steps_or_equal_to = True) for i, t in enumerate(tvals): for k in constants_containing_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({0}) for k in constants_less_than_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({1}) for k in ramps_containing_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({2}) for k in ramps_less_than_t[i]: for j, eig in enumerate(eigs): A[i,j] += ({3}) elif implementation == 'fortran': #note than all fortran subroutines are lowercase. if MUST_TRY_FORTRAN: from geotecha.speccon.ext_integrals import eload_sinlinear as fn else: try: from geotecha.speccon.ext_integrals import eload_sinlinear as fn except ImportError: fn = Eload_sinlinear A = fn(loadtim, loadmag, omega, phase, eigs, tvals, dT) else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos exp = np.exp A = np.zeros([len(tvals), len(eigs)], dtype=complex) (ramps_less_than_t, constants_less_than_t, steps_less_than_t, ramps_containing_t, constants_containing_t) = segment_containing_also_segments_less_than_xi(loadtim, loadmag, tvals, steps_or_equal_to = True) eig = eigs[:, None] for i, t in enumerate(tvals): k = constants_containing_t[i] if len(k): A[i, :] += np.sum({0}, axis=1) k = constants_less_than_t[i] if len(k): A[i, :] += np.sum({1}, axis=1) k = ramps_containing_t[i] if len(k): A[i, :] += np.sum({2}, axis=1) k = ramps_less_than_t[i] if len(k): A[i, :] += np.sum({3}, axis=1) return A""" text_fortran = """\ SUBROUTINE eload_sinlinear(loadtim, loadmag, omega, phase, & eigs, tvals, dT, a, neig, nload, nt) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nload INTEGER, intent(in) :: nt REAL(DP), intent(in), dimension(0:nload-1) :: loadtim REAL(DP), intent(in), dimension(0:nload-1) :: loadmag COMPLEX(DP), intent(in), dimension(0:neig-1) :: eigs REAL(DP), intent(in), dimension(0:nt-1) :: tvals REAL(DP), intent(in) :: dT REAL(DP), intent(in) :: omega REAL(DP), intent(in) :: phase COMPLEX(DP), intent(out), dimension(0:nt-1, 0:neig-1) :: a INTEGER :: i , j, k REAL(DP):: EPSILON a=0.0D0 EPSILON = 0.0000005D0 DO i = 0, nt-1 DO k = 0, nload-2 IF (tvals(i) < loadtim(k)) EXIT !t is before load step IF (tvals(i) >= loadtim(k + 1)) THEN !t is after the load step IF(ABS(loadtim(k) - loadtim(k + 1)) <= & (ABS(loadtim(k) + loadtim(k + 1))*EPSILON)) THEN !step load CONTINUE ELSEIF(ABS(loadmag(k) - loadmag(k + 1)) <= & (ABS(loadmag(k) + loadmag(k + 1))*EPSILON)) THEN !constant load DO j=0, neig-1 {0} END DO ELSE !ramp load DO j=0, neig-1 {1} END DO END IF ELSE !t is in the load step IF(ABS(loadmag(k) - loadmag(k + 1)) <= & (ABS(loadmag(k) + loadmag(k + 1))*EPSILON)) THEN !constant load DO j=0, neig-1 {2} END DO ELSE !ramp load DO j=0, neig-1 {3} END DO END IF END IF END DO END DO END SUBROUTINE """ fn = text_python.format( tw(within_constant, 6), tw(after_constant, 6), tw(within_ramp, 6), tw(after_ramp, 6)) mp3 = [(eig, eigs[j]), (t,tvals[i])] after_constant = after_constant.subs(mp3) after_ramp = after_ramp.subs(mp3) within_constant = within_constant.subs(mp3) within_ramp = within_ramp.subs(mp3) fn2 = text_fortran.format( fcode_one_large_expr(after_constant, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(after_ramp, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(within_constant, prepend='a(i, j) = a(i, j) + '), fcode_one_large_expr(within_ramp, prepend='a(i, j) = a(i, j) + ')) return fn, fn2
[docs]class SympyVarsFor1DSpectralDerivation(object): """Container for sympy vars, z, zt, zb, at, etc piecewise linear Spectral Galerkin integrations. Parameters ---------- linear_var : ['z', 'x', 'y'], optional Independent variable for the linear function specification. f(linear_var) = at+a_slope * (linear_var-linear_vart). Default linear_var='z'. slope : True/False, optional If True (default), then the linear functions will be defined with a lumped slope term, e.g. a = atop + a_slope * (z - ztop) compared to if slope=False, where a = atop + (abot-atop)/(zbot-ztop)*(z-ztop). You will have to define a_slope in your code template, e.g. Python loops: a_slope = (ab[layer]-at[layer])/(zb[layer]-zt[layer]). Fortran loops: a_slope = (ab(layer)-at(layer)/(zb(layer)-zt(layer)). Vectorised: a_slope = (ab-at)/(zb-zt). Attributes ---------- x, y, z : sympy.Symbol Independent variables. xtop, xbot, ytop, ybot, ztop, zbot : sympy.Symbol Symbols used in expressions to be integrated with sympy.integrate. After the integration these variables are usually replaced with the relevant xt, xb, yt etc. or xt[layer], yb[layer] etc. These substitutions can be made using `map_to_add_index' or `map_top_to_t_bot_to_b`. i, j, k , layer : sympy.tensor.Idx Index variables. xt, xb, yt, yb, zt, zb : sympy.tensor.IndexedBase Variables for values at top and bottom of layer. Used to define linear relationships. These variable usually replace xtop, xbot after integrations. The reason they are not used before integration is thatsympy doesn't seem to like the sympy.tensor.IndexedBase for integrations. Substitutions can be made using `map_to_add_index' or `map_top_to_t_bot_to_b`. at, ab, a, a_slope: sympy.Symbol and sympy.Expr Variables to define linear relationships. If `slope`=True, a = atop + a_slope * (z - ztop) If `slope`=False, a = atop + (abot - atop)/(zbot - ztop) * (z - ztop). Where z and ztop may change according to `linear_var`. bt, bb, b, b_slope: sympy.Symbol and sympy.Expr Variables to define linear relationships. If `slope`=True, b = atop + b_slope * (z - ztop) If `slope`=False, b = btop + (bbot - btop)/(zbot - ztop) * (z - ztop). Where z and ztop may change according to `linear_var`. ct, cb, c, c_slope: sympy.Symbol and sympy.Expr Variables to define linear relationships. If `slope`=True, c = ctop + c_slope * (z - ztop) If `slope`=False, c = ctop + (cbot - ctop)/(zbot - ztop) * (z - ztop). Where z and ztop may change according to `linear_var`. map_to_add_index : list of 2 element tuples A list to be used with the subs method of sympy expressions to add a index variables to the variables. A typical entries in `map_to_add_index` would be [(mi, m[i]), (mj, m[j]), (atop, at[layer]), (ztop, zt[layer]), ...]. Use this to after an integration invoving ztop, atop etc. to format the expression for use in function with loops. map_top_to_t_bot_to_b : list of 2 element tuples A list to be used with the subs method of sympy expressions to add change 'ztop' to 'zt', 'abot' to 'at' etc. Typical entries in `map_top_to_t_bot_to_b` would be [(atop, at), (ztop, zt), ...]. Use this to after an integration invoving ztop, atop etc. to format the expression for use in a vectorised function. mi, mj : sympy.Symbol Variable for row and column eigs. m : sympy.tensor.IndexedBase IndexedBaseVariable of eigs. used for m[i], and m[j]. Examples -------- >>> v = SympyVarsFor1DSpectralDerivation() >>> v.a a_slope*(z - ztop) + atop >>> v.b b_slope*(z - ztop) + btop >>> v.c c_slope*(z - ztop) + ctop >>> v.map_to_add_index [(mi, m[i]), (mj, m[j]), (atop, at[layer]), (abot, ab[layer]), (btop, bt[layer]), (bbot, bb[layer]), (ctop, ct[layer]), (cbot, cb[layer]), (ztop, zt[layer]), (zbot, zb[layer])] >>> v.a.subs(v.map_to_add_index) a_slope*(z - zt[layer]) + at[layer] >>> v.b.subs(v.map_top_to_t_bot_to_b) b_slope*(z - zt) + bt >>> v.mi mi >>> v.mi.subs(v.map_to_add_index) m[i] >>> v.mi.subs(v.map_top_to_t_bot_to_b) mi >>> v = SympyVarsFor1DSpectralDerivation(linear_var='x', slope=False) >>> v.a atop + (abot - atop)*(x - xtop)/(xbot - xtop) """ def __init__(self, linear_var='z', slope=True): import sympy #integration variables self.x, self.y, self.z = sympy.symbols('x,y,z') self.xtop, self.xbot = sympy.symbols('xtop,xbot', nonzero=True) self.ytop, self.ybot = sympy.symbols('ytop,ybot', nonzero=True) self.ztop, self.zbot = sympy.symbols('ztop,zbot', nonzero=True) self.atop, self.abot = sympy.symbols('atop,abot', nonzero=True) self.btop, self.bbot = sympy.symbols('btop,bbot', nonzero=True) self.ctop, self.cbot = sympy.symbols('ctop,cbot', nonzero=True) # indexes self.i = sympy.tensor.Idx('i') self.j = sympy.tensor.Idx('j') self.k = sympy.tensor.Idx('k') self.layer = sympy.tensor.Idx('layer') self.xt = sympy.tensor.IndexedBase('xt') self.xb = sympy.tensor.IndexedBase('xb') self.yt = sympy.tensor.IndexedBase('yt') self.yb = sympy.tensor.IndexedBase('yb') self.zt = sympy.tensor.IndexedBase('zt') self.zb = sympy.tensor.IndexedBase('zb') self.m = sympy.tensor.IndexedBase('m') self.mi, self.mj = sympy.symbols('mi,mj', nonzero=True) mmap_to_add_index = [(self.mi, self.m[self.i]), (self.mj, self.m[self.j])] #linear f(linear_var) self.linear_var = linear_var _x = getattr(self, linear_var) _xtop = getattr(self, linear_var + 'top') _xbot = getattr(self, linear_var + 'bot') _xt = getattr(self, linear_var + 't') _xb = getattr(self, linear_var + 'b') _xmap_to_add_index = [(_xtop, _xt[self.layer]), (_xbot, _xb[self.layer])] _xmap_top_to_t_bot_to_b = [(_xtop, _xt), (_xbot, _xb)] self.at = sympy.tensor.IndexedBase('at') self.ab = sympy.tensor.IndexedBase('ab') self.a_slope = sympy.symbols('a_slope') if slope: self.a = (self.atop + self.a_slope * (_x - _xtop)) else: self.a = (self.atop + (self.abot - self.atop) / (_xbot - _xtop) * (_x - _xtop)) amap_to_add_index = [(self.atop, self.at[self.layer]), (self.abot, self.ab[self.layer])] amap_top_to_t_bot_to_b = [(self.atop, self.at), (self.abot, self.ab)] self.bt = sympy.tensor.IndexedBase('bt') self.bb = sympy.tensor.IndexedBase('bb') self.b_slope = sympy.symbols('b_slope') if slope: self.b = (self.btop + self.b_slope * (_x - _xtop)) else: self.b = (self.btop + (self.bbot - self.btop) / (_xbot - _xtop) * (_x - _xtop)) bmap_to_add_index = [(self.btop, self.bt[self.layer]), (self.bbot, self.bb[self.layer])] bmap_top_to_t_bot_to_b = [(self.btop, self.bt), (self.bbot, self.bb)] self.ct = sympy.tensor.IndexedBase('ct') self.cb = sympy.tensor.IndexedBase('cb') self.c_slope = sympy.symbols('c_slope') if slope: self.c = (self.ctop + self.c_slope * (_x - _xtop)) else: self.c = (self.ctop + (self.cbot - self.ctop) / (_xbot - _xtop) * (_x - _xtop)) cmap_to_add_index = [(self.ctop, self.ct[self.layer]), (self.cbot, self.cb[self.layer])] cmap_top_to_t_bot_to_b = [(self.ctop, self.ct), (self.cbot, self.cb)] # make sure mmap... is the first entry!!!!!!! self.map_to_add_index = (mmap_to_add_index + amap_to_add_index + bmap_to_add_index + cmap_to_add_index + _xmap_to_add_index) self.map_top_to_t_bot_to_b = (amap_top_to_t_bot_to_b + bmap_top_to_t_bot_to_b + cmap_top_to_t_bot_to_b + _xmap_top_to_t_bot_to_b)
[docs]def dim1sin_af_linear_implementations(): """Code generation for Integration of sin(mi * z) * a(z) * sin(mj * z) between ztop and zbot where a(z) is piecewise linear. Code is generated that will produce a square array with the appropriate integrals at each location. Paste the resulting code (at least the loops) into `dim1sin_af_linear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. Needs to be compiled with f2py. Notes ----- The `dim1sin_af_linear` matrix, :math:`A` is given by: .. math:: \\mathbf{A}_{i,j}=\\int_{0}^1{{a\\left(z\\right)}\\phi_i\\phi_j\\,dz} where the basis function :math:`\\phi_i` is given by: .. math:: \\phi_i\\left(z\\right)=\\sin\\left({m_i}z\\right) and :math:`a\\left(z\\right)` is a piecewise linear function with respect to :math:`z`, that within a layer are defined by: .. math:: a\\left(z\\right) = a_t+\\frac{a_b-a_t}{z_b-z_t}\\left(z-z_t\\right) with :math:`t` and :math:`b` subscripts representing 'top' and 'bottom' of each layer respectively. See Also -------- geotecha.speccon.integrals.dim1sin_af_linear : Resulting function. geotecha.speccon.integrals.pdim1sin_af_linear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.dim1sin_af_linear : Resulting fortran function. """ v = SympyVarsFor1DSpectralDerivation('z') integ_kwargs = dict(risch=False, conds='none') phi_i = sympy.sin(v.mi * v.z) phi_j = sympy.sin(v.mj * v.z) fdiag = sympy.integrate(v.a * phi_i * phi_i, v.z, **integ_kwargs) fdiag_loops = fdiag.subs(v.z, v.zbot) - fdiag.subs(v.z, v.ztop) fdiag_loops = fdiag_loops.subs(v.map_to_add_index) fdiag_vector = fdiag.subs(v.z, v.zbot) - fdiag.subs(v.z, v.ztop) fdiag_vector = fdiag_vector.subs(v.map_top_to_t_bot_to_b) foff = sympy.integrate(v.a * phi_j * phi_i, v.z, **integ_kwargs) foff_loops = foff.subs(v.z, v.zbot) - foff.subs(v.z, v.ztop) foff_loops = foff_loops.subs(v.map_to_add_index) foff_vector = foff.subs(v.z, v.zbot) - foff.subs(v.z, v.ztop) foff_vector = foff_vector.subs(v.map_top_to_t_bot_to_b) text_python = """def dim1sin_af_linear(m, at, ab, zt, zb, implementation='vectorized'): #import numpy as np #import this at module level #import math #import this at module level m = np.asarray(m) at = np.asarray(at) ab = np.asarray(ab) zt = np.asarray(zt) zb = np.asarray(zb) neig = len(m) if implementation == 'scalar': sin = math.sin cos = math.cos A = np.zeros([neig, neig], float) nlayers = len(zt) for layer in range(nlayers): a_slope = (ab[layer] - at[layer]) / (zb[layer] - zt[layer]) for i in range(neig): A[i, i] += ({0}) for i in range(neig-1): for j in range(i + 1, neig): A[i, j] += ({1}) #A is symmetric for i in range(neig - 1): for j in range(i + 1, neig): A[j, i] = A[i, j] elif implementation == 'fortran': if MUST_TRY_FORTRAN: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_af_linear(m, at, ab, zt, zb) try: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_af_linear(m, at, ab, zt, zb) except ImportError: A = dim1sin_af_linear(m, at, ab, zt, zb, implementation='vectorized') else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos A = np.zeros([neig, neig], float) diag = np.diag_indices(neig) triu = np.triu_indices(neig, k = 1) tril = (triu[1], triu[0]) a_slope = (ab - at) / (zb - zt) mi = m[:, np.newaxis] A[diag] = np.sum({2}, axis=1) mi = m[triu[0]][:, np.newaxis] mj = m[triu[1]][:, np.newaxis] A[triu] = np.sum({3}, axis=1) #A is symmetric A[tril] = A[triu] return A""" # note the the i=j part in the fortran loop below is because # I changed the loop order from layer, i,j to layer, j,i which is # i think faster as first index of a fortran array loops faster # my sympy code is mased on m[i], hence the need for i=j. text_fortran = """ SUBROUTINE dim1sin_af_linear(m, at, ab, zt, zb, a, neig, nlayers) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nlayers REAL(DP), intent(in), dimension(0:neig-1) ::m REAL(DP), intent(in), dimension(0:nlayers-1) :: at REAL(DP), intent(in), dimension(0:nlayers-1) :: ab REAL(DP), intent(in), dimension(0:nlayers-1) :: zt REAL(DP), intent(in), dimension(0:nlayers-1) :: zb REAL(DP), intent(out), dimension(0:neig-1, 0:neig-1) :: a INTEGER :: i , j, layer REAL(DP) :: a_slope a=0.0D0 DO layer = 0, nlayers-1 a_slope = (ab(layer) - at(layer)) / (zb(layer) - zt(layer)) DO j = 0, neig-1 i=j {0} DO i = j+1, neig-1 {1} END DO END DO END DO DO j = 0, neig -2 DO i = j + 1, neig-1 a(j,i) = a(i, j) END DO END DO END SUBROUTINE""" fn = text_python.format(tw(fdiag_loops,5), tw(foff_loops,6), tw(fdiag_vector,3), tw(foff_vector,3)) fn2 = text_fortran.format(fcode_one_large_expr(fdiag_loops, prepend='a(i, i) = a(i, i) + '), fcode_one_large_expr(foff_loops, prepend='a(i, j) = a(i, j) + ')) return fn, fn2
[docs]def dim1sin_abf_linear_implementations(): """Code generation for Integration of sin(mi * z) * a(z) * a(z) * sin(mj * z) between ztop and zbot where a(z) is piecewise linear. Code is generated that will produce a square array with the appropriate integrals at each location. Paste the resulting code (at least the loops) into `dim1sin_abf_linear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. Needs to be compiled with f2py. Notes ----- The `dim1sin_abf_linear` matrix, :math:`A` is given by: .. math:: \\mathbf{A}_{i,j}=\\int_{0}^1{{a\\left(z\\right)}{b\\left(z\\right)}\\phi_i\\phi_j\\,dz} where the basis function :math:`\\phi_i` is given by: .. math:: \\phi_i\\left(z\\right)=\\sin\\left({m_i}z\\right) and :math:`a\\left(z\\right)` and :math:`b\\left(z\\right)` are piecewise linear functions with respect to :math:`z`, that within a layer are defined by: .. math:: a\\left(z\\right) = a_t+\\frac{a_b-a_t}{z_b-z_t}\\left(z-z_t\\right) with :math:`t` and :math:`b` subscripts representing 'top' and 'bottom' of each layer respectively. See Also -------- geotecha.speccon.integrals.dim1sin_abf_linear : Resulting function. geotecha.speccon.integrals.pdim1sin_abf_linear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.dim1sin_abf_linear : Resulting fortran function. """ v = SympyVarsFor1DSpectralDerivation('z') integ_kwargs = dict(risch=False, conds='none') phi_i = sympy.sin(v.mi * v.z) phi_j = sympy.sin(v.mj * v.z) # fdiag = sympy.integrate(p['a'] * p['b'] * phi_i * phi_i, z) fdiag = sympy.integrate(v.a * v.b * phi_i * phi_i, v.z, **integ_kwargs) fdiag_loops = fdiag.subs(v.z, v.zbot) - fdiag.subs(v.z, v.ztop) fdiag_loops = fdiag_loops.subs(v.map_to_add_index) fdiag_vector = fdiag.subs(v.z, v.zbot) - fdiag.subs(v.z, v.ztop) fdiag_vector = fdiag_vector.subs(v.map_top_to_t_bot_to_b) # foff = sympy.integrate(p['a'] * p['b'] * phi_j * phi_i, z) foff = sympy.integrate(v.a * v.b * phi_j * phi_i, v.z, **integ_kwargs) foff_loops = foff.subs(v.z, v.zbot) - foff.subs(v.z, v.ztop) foff_loops = foff_loops.subs(v.map_to_add_index) foff_vector = foff.subs(v.z, v.zbot) - foff.subs(v.z, v.ztop) foff_vector = foff_vector.subs(v.map_top_to_t_bot_to_b) text_python = """def dim1sin_abf_linear(m, at, ab, bt, bb, zt, zb, implementation='vectorized'): #import numpy as np #import this at module level #import math #import this at module level m = np.asarray(m) at = np.asarray(at) ab = np.asarray(ab) bt = np.asarray(bt) bb = np.asarray(bb) zt = np.asarray(zt) zb = np.asarray(zb) neig = len(m) if implementation == 'scalar': sin = math.sin cos = math.cos A = np.zeros([neig, neig], float) nlayers = len(zt) for layer in range(nlayers): a_slope = (ab[layer] - at[layer]) / (zb[layer] - zt[layer]) b_slope = (bb[layer] - bt[layer]) / (zb[layer] - zt[layer]) for i in range(neig): A[i, i] += ({0}) for i in range(neig-1): for j in range(i + 1, neig): A[i, j] += ({1}) #A is symmetric for i in range(neig - 1): for j in range(i + 1, neig): A[j, i] = A[i, j] elif implementation == 'fortran': if MUST_TRY_FORTRAN: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_abf_linear(m, at, ab, bt, bb, zt, zb) else: try: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_abf_linear(m, at, ab, bt, bb, zt, zb) except ImportError: A = dim1sin_abf_linear(m, at, ab, bt, bb, zt, zb, implementation='vectorized') else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos A = np.zeros([neig, neig], float) diag = np.diag_indices(neig) triu = np.triu_indices(neig, k = 1) tril = (triu[1], triu[0]) a_slope = (ab - at) / (zb - zt) b_slope = (bb - bt) / (zb - zt) mi = m[:, np.newaxis] A[diag] = np.sum({2}, axis=1) mi = m[triu[0]][:, np.newaxis] mj = m[triu[1]][:, np.newaxis] A[triu] = np.sum({3}, axis=1) #A is symmetric A[tril] = A[triu] return A""" # note the the i=j part in the fortran loop below is because # I changed the loop order from layer, i,j to layer, j,i which is # i think faster as first index of a fortran array loops faster # my sympy code is mased on m[i], hence the need for i=j. text_fortran = """\ SUBROUTINE dim1sin_abf_linear(m, at, ab, bt, bb, zt, zb, a, & neig, nlayers) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nlayers REAL(DP), intent(in), dimension(0:neig-1) ::m REAL(DP), intent(in), dimension(0:nlayers-1) :: at,ab,bt,bb,zt,zb ! REAL(DP), intent(in), dimension(0:nlayers-1) :: at ! REAL(DP), intent(in), dimension(0:nlayers-1) :: ab ! REAL(DP), intent(in), dimension(0:nlayers-1) :: bt ! REAL(DP), intent(in), dimension(0:nlayers-1) :: bb ! REAL(DP), intent(in), dimension(0:nlayers-1) :: zt ! REAL(DP), intent(in), dimension(0:nlayers-1) :: zb REAL(DP), intent(out), dimension(0:neig-1, 0:neig-1) :: a INTEGER :: i , j, layer REAL(DP) :: a_slope, b_slope a=0.0D0 DO layer = 0, nlayers-1 a_slope = (ab(layer) - at(layer)) / (zb(layer) - zt(layer)) b_slope = (bb(layer) - bt(layer)) / (zb(layer) - zt(layer)) DO j = 0, neig-1 i=j {0} DO i = j+1, neig-1 {1} END DO END DO END DO DO j = 0, neig -2 DO i = j + 1, neig-1 a(j,i) = a(i, j) END DO END DO END SUBROUTINE""" fn = text_python.format(tw(fdiag_loops,5), tw(foff_loops,6), tw(fdiag_vector,3), tw(foff_vector,3)) fn2 = text_fortran.format(fcode_one_large_expr(fdiag_loops, prepend='a(i, i) = a(i, i) + '), fcode_one_large_expr(foff_loops, prepend='a(i, j) = a(i, j) + ')) return fn, fn2
[docs]def dim1sin_D_aDf_linear_implementations(): """Code generation for Integration of sin(mi * z) * D[a(z) * D[sin(mj * z),z],z] between ztop and zbot where a(z) is piecewise linear functions of z. Code is generated that will produce a square array with the appropriate integrals at each location. Paste the resulting code (at least the loops) into `dim1sin_abf_linear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. Needs to be compiled with f2py. See Also -------- geotecha.speccon.integrals.dim1sin_D_aDf_linear : Resulting function. geotecha.speccon.integrals.pdim1sin_D_aDf_linear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.dim1sin_d_adf_linear : Resulting fortran function. Notes ----- The `dim1sin_D_aDf_linear` matrix, :math:`A` is given by: .. math:: \\mathbf{A}_{i,j}=\\int_{0}^1{\\frac{d}{dz}\\left({a\\left(z\\right)}\\frac{d\\phi_j}{dz}\\right)\\phi_i\\,dz} where the basis function :math:`\\phi_i` is given by: .. math:: \\phi_i\\left(z\\right)=\\sin\\left({m_i}z\\right) and :math:`a\\left(z\\right)` is a piecewise linear functions with respect to :math:`z`, that within a layer is defined by: .. math:: a\\left(z\\right) = a_t+\\frac{a_b-a_t}{z_b-z_t}\\left(z-z_t\\right) with :math:`t` and :math:`b` subscripts representing 'top' and 'bottom' of each layer respectively. To make the above integration simpler we integate by parts to get: .. math:: \\mathbf{A}_{i,j}= \\left.\\phi_i{a\\left(z\\right)}\\frac{d\\phi_j}{dz}\\right|_{z=0}^{z=1} -\\int_{0}^1{{a\\left(z\\right)}\\frac{d\\phi_j}{dz}\\frac{d\\phi_i}{dz}\\,dz} In this case the sine basis functions means the left term in the above equation is zero, leaving us with .. math:: \\mathbf{A}_{i,j}= -\\int_{0}^1{{a\\left(z\\right)}\\frac{d\\phi_j}{dz}\\frac{d\\phi_i}{dz}\\,dz} """ # NOTE: remember that fortran does not distinguish between upper and lower # case. When f2py wraps a fortran function with upper case letters then # upper case letters will be converted to lower case. e.g. Therefore when # calling a fortran function called if fortran fn is # 'dim1sin_D_aDf_linear' f2py will wrap it as 'dim1sin_d_adf_linear' v = SympyVarsFor1DSpectralDerivation('z') integ_kwargs = dict(risch=False, conds='none') phi_i = sympy.sin(v.mi * v.z) phi_j = sympy.sin(v.mj * v.z) fdiag = sympy.integrate(-sympy.diff(phi_i, v.z) * v.a * sympy.diff(phi_i, v.z), v.z, **integ_kwargs) fdiag_loops = fdiag.subs(v.z, v.zbot) - fdiag.subs(v.z, v.ztop) fdiag_loops = fdiag_loops.subs(v.map_to_add_index) fdiag_vector = fdiag.subs(v.z, v.zbot) - fdiag.subs(v.z, v.ztop) fdiag_vector = fdiag_vector.subs(v.map_top_to_t_bot_to_b) foff = sympy.integrate(-sympy.diff(phi_i, v.z) * v.a * sympy.diff(phi_j, v.z), v.z, **integ_kwargs) foff_loops = foff.subs(v.z, v.zbot) - foff.subs(v.z, v.ztop) foff_loops = foff_loops.subs(v.map_to_add_index) foff_vector = foff.subs(v.z, v.zbot) - foff.subs(v.z, v.ztop) foff_vector = foff_vector.subs(v.map_top_to_t_bot_to_b) text_python = """def dim1sin_D_aDf_linear(m, at, ab, zt, zb, implementation='vectorized'): #import numpy as np #import this at module level #import math #import this at module level m = np.asarray(m) at = np.asarray(at) ab = np.asarray(ab) zt = np.asarray(zt) zb = np.asarray(zb) neig = len(m) if implementation == 'scalar': sin = math.sin cos = math.cos A = np.zeros([neig, neig], float) nlayers = len(zt) for layer in range(nlayers): a_slope = (ab[layer] - at[layer]) / (zb[layer] - zt[layer]) for i in range(neig): A[i, i] += ({0}) for i in range(neig-1): for j in range(i + 1, neig): A[i, j] += ({1}) #A is symmetric for i in range(neig - 1): for j in range(i + 1, neig): A[j, i] = A[i, j] elif implementation == 'fortran': if MUST_TRY_FORTRAN: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_d_adf_linear(m, at, ab, zt, zb) else: try: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_d_adf_linear(m, at, ab, zt, zb) except ImportError: A = dim1sin_D_aDf_linear(m, at, ab, zt, zb, implementation='vectorized') else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos A = np.zeros([neig, neig], float) diag = np.diag_indices(neig) triu = np.triu_indices(neig, k = 1) tril = (triu[1], triu[0]) a_slope = (ab - at) / (zb - zt) mi = m[:, np.newaxis] A[diag] = np.sum({2}, axis=1) mi = m[triu[0]][:, np.newaxis] mj = m[triu[1]][:, np.newaxis] A[triu] = np.sum({3}, axis=1) #A is symmetric A[tril] = A[triu] return A""" # note the the i=j part in the fortran loop below is because # I changed the loop order from layer, i,j to layer, j,i which is # i think faster as first index of a fortran array loops faster # my sympy code is mased on m[i], hence the need for i=j. text_fortran = """ SUBROUTINE dim1sin_D_aDf_linear(m, at, ab, zt, zb, a, neig, nlayers) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nlayers REAL(DP), intent(in), dimension(0:neig-1) ::m REAL(DP), intent(in), dimension(0:nlayers-1) :: at REAL(DP), intent(in), dimension(0:nlayers-1) :: ab REAL(DP), intent(in), dimension(0:nlayers-1) :: zt REAL(DP), intent(in), dimension(0:nlayers-1) :: zb REAL(DP), intent(out), dimension(0:neig-1, 0:neig-1) :: a INTEGER :: i , j, layer REAL(DP) :: a_slope a=0.0D0 DO layer = 0, nlayers-1 a_slope = (ab(layer) - at(layer)) / (zb(layer) - zt(layer)) DO j = 0, neig-1 i=j {0} DO i = j+1, neig-1 {1} END DO END DO END DO DO j = 0, neig -2 DO i = j + 1, neig-1 a(j,i) = a(i, j) END DO END DO END SUBROUTINE""" fn = text_python.format(tw(fdiag_loops,5), tw(foff_loops,6), tw(fdiag_vector,3), tw(foff_vector,3)) fn2 = text_fortran.format(fcode_one_large_expr(fdiag_loops, prepend='a(i, i) = a(i, i) + '), fcode_one_large_expr(foff_loops, prepend='a(i, j) = a(i, j) + ')) return fn, fn2
[docs]def dim1sin_ab_linear_implementations(): """Code generation for Integration of sin(mi * z) * a(z) * b(z) between ztop and zbot where a(z) and b(z) are piecewise linear functions of z. Code is generated that will produce a square array with the appropriate integrals at each location. Paste the resulting code (at least the loops) into `dim1sin_ab_linear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. Needs to be compiled with f2py. See Also -------- geotecha.speccon.integrals.dim1sin_ab_linear : Resulting function. geotecha.speccon.integrals.pdim1sin_ab_linear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.dim1sin_ab_linear : Resulting fortran function. Notes ----- The `dim1sin_ab_linear` which should be treated as a column vector, :math:`A` is given by: .. math:: \\mathbf{A}_{i}=\\int_{0}^1{{a\\left(z\\right)}{b\\left(z\\right)}\\phi_i\\,dz} where the basis function :math:`\\phi_i` is given by: .. math:: \\phi_i\\left(z\\right)=\\sin\\left({m_i}z\\right) and :math:`a\\left(z\\right)` and :math:`b\\left(z\\right)` are piecewise linear functions with respect to :math:`z`, that within a layer are defined by: .. math:: a\\left(z\\right) = a_t+\\frac{a_b-a_t}{z_b-z_t}\\left(z-z_t\\right) with :math:`t` and :math:`b` subscripts representing 'top' and 'bottom' of each layer respectively. """ v = SympyVarsFor1DSpectralDerivation('z') integ_kwargs = dict(risch=False, conds='none') phi_i = sympy.sin(v.mi * v.z) # phi_j = sympy.sin(v.mj * v.z) fcol = sympy.integrate(v.a * v.b * phi_i, v.z, **integ_kwargs) fcol_loops = fcol.subs(v.z, v.zbot) - fcol.subs(v.z, v.ztop) fcol_loops = fcol_loops.subs(v.map_to_add_index) fcol_vector = fcol.subs(v.z, v.zbot) - fcol.subs(v.z, v.ztop) fcol_vector = fcol_vector.subs(v.map_top_to_t_bot_to_b) # mpv, p = create_layer_sympy_var_and_maps_vectorized(layer_prop=['z','a', 'b']) # mp, p = create_layer_sympy_var_and_maps(layer_prop=['z','a','b']) # # phi_i = sympy.sin(mi * z) # fcol = sympy.integrate(p['a'] * p['b'] * phi_i, z) # fcol_loops = fcol.subs(z, mp['zbot']) - fcol.subs(z, mp['ztop']) # fcol_loops = fcol_loops.subs(mp) # fcol_vector = fcol.subs(z, mpv['zbot']) - fcol.subs(z, mpv['ztop']) # fcol_vector = fcol_vector.subs(mpv) text_python = """def dim1sin_ab_linear(m, at, ab, bt, bb, zt, zb, implementation='vectorized'): #import numpy as np #import this at module level #import math #import this at module level m = np.asarray(m) at = np.asarray(at) ab = np.asarray(ab) bt = np.asarray(bt) bb = np.asarray(bb) zt = np.asarray(zt) zb = np.asarray(zb) neig = len(m) if implementation == 'scalar': sin = math.sin cos = math.cos A = np.zeros(neig, float) nlayers = len(zt) for layer in range(nlayers): a_slope = (ab[layer] - at[layer]) / (zb[layer] - zt[layer]) b_slope = (bb[layer] - bt[layer]) / (zb[layer] - zt[layer]) for i in range(neig): A[i] += ({0}) elif implementation == 'fortran': if MUST_TRY_FORTRAN: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_ab_linear(m, at, ab, bt, bb, zt, zb) else: try: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_ab_linear(m, at, ab, bt, bb, zt, zb) except ImportError: A = dim1sin_ab_linear(m, at, ab, bt, bb, zt, zb, implementation='vectorized') else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos A = np.zeros(neig, float) a_slope = (ab - at) / (zb - zt) b_slope = (bb - bt) / (zb - zt) mi = m[:, np.newaxis] A[:] = np.sum({1}, axis=1) return A""" # note the the i=j part in the fortran loop below is because # I changed the loop order from layer, i,j to layer, j,i which is # i think faster as first index of a fortran array loops faster # my sympy code is mased on m[i], hence the need for i=j. text_fortran = """\ SUBROUTINE dim1sin_ab_linear(m, at, ab, bt, bb, zt, zb, & a, neig, nlayers) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nlayers REAL(DP), intent(in), dimension(0:neig-1) ::m REAL(DP), intent(in), dimension(0:nlayers-1) :: at,ab,bt,bb,zt,zb REAL(DP), intent(out), dimension(0:neig-1) :: a INTEGER :: i, layer REAL(DP) :: a_slope, b_slope a=0.0D0 DO layer = 0, nlayers-1 a_slope = (ab(layer) - at(layer)) / (zb(layer) - zt(layer)) b_slope = (bb(layer) - bt(layer)) / (zb(layer) - zt(layer)) DO i = 0, neig-1 {0} END DO END DO END SUBROUTINE""" fn = text_python.format(tw(fcol_loops,5), tw(fcol_vector,3)) fn2 = text_fortran.format(fcode_one_large_expr(fcol_loops, prepend='a(i) = a(i) + ')) return fn, fn2
[docs]def dim1sin_abc_linear_implementations(): """Code generation for Integrations of sin(mi * z) * a(z) * b(z) * c(z) between ztop and zbot where a(z), b(z), c(z) are piecewise linear functions of z. Code is generated that will produce a 1d array with the appropriate integrals at each location. Paste the resulting code (at least the loops) into `dim1sin_abc_linear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. Needs to be compiled with f2py. See Also -------- geotecha.speccon.integrals.dim1sin_abc_linear : Resulting function. geotecha.speccon.integrals.pdim1sin_abc_linear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.dim1sin_abc_linear : Resulting fortran function. Notes ----- The `dim1sin_abc_linear` which should be treated as a column vector, :math:`A` is given by: .. math:: \\mathbf{A}_{i}=\\int_{0}^1{{a\\left(z\\right)}{b\\left(z\\right)}{c\\left(z\\right)}\\phi_i\\,dz} where the basis function :math:`\\phi_i` is given by: .. math:: \\phi_i\\left(z\\right)=\\sin\\left({m_i}z\\right) and :math:`a\\left(z\\right)`, :math:`b\\left(z\\right)`, and :math:`c\\left(z\\right)` are piecewise linear functions with respect to :math:`z`, that within a layer are defined by: .. math:: a\\left(z\\right) = a_t+\\frac{a_b-a_t}{z_b-z_t}\\left(z-z_t\\right) with :math:`t` and :math:`b` subscripts representing 'top' and 'bottom' of each layer respectively. """ v = SympyVarsFor1DSpectralDerivation('z') integ_kwargs = dict(risch=False, conds='none') phi_i = sympy.sin(v.mi * v.z) fcol = sympy.integrate(v.a * v.b * v.c* phi_i, v.z, **integ_kwargs) fcol_loops = fcol.subs(v.z, v.zbot) - fcol.subs(v.z, v.ztop) fcol_loops = fcol_loops.subs(v.map_to_add_index) fcol_vector = fcol.subs(v.z, v.zbot) - fcol.subs(v.z, v.ztop) fcol_vector = fcol_vector.subs(v.map_top_to_t_bot_to_b) text_python = """def dim1sin_abc_linear(m, at, ab, bt, bb, ct, cb, zt, zb, implementation='vectorized'): #import numpy as np #import this at module level #import math #import this at module level m = np.asarray(m) at = np.asarray(at) ab = np.asarray(ab) bt = np.asarray(bt) bb = np.asarray(bb) ct = np.asarray(ct) cb = np.asarray(cb) zt = np.asarray(zt) zb = np.asarray(zb) neig = len(m) if implementation == 'scalar': sin = math.sin cos = math.cos A = np.zeros(neig, float) nlayers = len(zt) for layer in range(nlayers): a_slope = (ab[layer] - at[layer]) / (zb[layer] - zt[layer]) b_slope = (bb[layer] - bt[layer]) / (zb[layer] - zt[layer]) c_slope = (cb[layer] - ct[layer]) / (zb[layer] - zt[layer]) for i in range(neig): A[i] += ({0}) elif implementation == 'fortran': if MUST_TRY_FORTRAN: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_abc_linear(m, at, ab, bt, bb, ct, cb, zt, zb) else: try: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_abc_linear(m, at, ab, bt, bb, ct, cb, zt, zb) except ImportError: A = dim1sin_abc_linear(m, at, ab, bt, bb, ct, cb, zt, zb, implementation='vectorized') else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos A = np.zeros(neig, float) a_slope = (ab - at) / (zb - zt) b_slope = (bb - bt) / (zb - zt) c_slope = (cb - ct) / (zb - zt) mi = m[:, np.newaxis] A[:] = np.sum({1}, axis=1) return A""" # note the the i=j part in the fortran loop below is because # I changed the loop order from layer, i,j to layer, j,i which is # i think faster as first index of a fortran array loops faster # my sympy code is mased on m[i], hence the need for i=j. text_fortran = """\ SUBROUTINE dim1sin_abc_linear(m, at, ab, bt, bb, ct, cb, & zt, zb, a, neig, nlayers) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nlayers REAL(DP), intent(in), dimension(0:neig-1) ::m REAL(DP), intent(in), dimension(0:nlayers-1) :: at,ab,bt,bb, & ct,cb,zt,zb REAL(DP), intent(out), dimension(0:neig-1) :: a INTEGER :: i, layer REAL(DP) :: a_slope, b_slope, c_slope a=0.0D0 DO layer = 0, nlayers-1 a_slope = (ab(layer) - at(layer)) / (zb(layer) - zt(layer)) b_slope = (bb(layer) - bt(layer)) / (zb(layer) - zt(layer)) c_slope = (cb(layer) - ct(layer)) / (zb(layer) - zt(layer)) DO i = 0, neig-1 {0} END DO END DO END SUBROUTINE""" fn = text_python.format(tw(fcol_loops,5), tw(fcol_vector,3)) fn2 = text_fortran.format(fcode_one_large_expr(fcol_loops, prepend='a(i) = a(i) + ')) return fn, fn2
[docs]def dim1sin_D_aDb_linear_implementations(): """Code generation for Integrations of `sin(mi * z) * D[a(z) * D[b(z), z], z]` between ztop and zbot where a(z) is a piecewisepiecewise linear function of z, and b(z) is a linear function of z. Code is generated that will produce a 1d array with the appropriate integrals at each location. Paste the resulting code (at least the loops) into `dim1sin_D_aDb_linear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. .. warning:: The functions produced are set up to accept the b(z) input as piecewise linear, i.e. zt, zb, bt, bb etc. It is up to the user to ensure that the bt and bb are such that they define a continuous linear function. eg. to define b(z)=z+1 then use zt=[0,0.4], zb=[0.4, 1], bt=[1,1.4], bb=[1.4,2]. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. Needs to be compiled with f2py. See Also -------- geotecha.speccon.integrals.dim1sin_D_aDb_linear : Resulting function. geotecha.speccon.integrals.pdim1sin_D_aDb_linear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.dim1sin_D_aDb_linear : Resulting fortran function. Notes ----- The `dim1sin_D_aDb_linear` which should be treated as a column vector, :math:`A` is given by: .. math:: \\mathbf{A}_{i}=\\int_{0}^1{\\frac{d}{dz}\\left({a\\left(z\\right)}\\frac{d}{dz}{b\\left(z\\right)}\\right)\\phi_i\\,dz} where the basis function :math:`\\phi_i` is given by: .. math:: \\phi_i\\left(z\\right)=\\sin\\left({m_i}z\\right) and :math:`a\\left(z\\right)` is a piecewise linear functions with respect to :math:`z`, that within a layer is defined by: .. math:: a\\left(z\\right) = a_t+\\frac{a_b-a_t}{z_b-z_t}\\left(z-z_t\\right) with :math:`t` and :math:`b` subscripts representing 'top' and 'bottom' of each layer respectively. :math:`b\\left(z\\right)` is a linear function of :math:`z` defined by .. math:: b\\left(z\\right) = b_t+\\left({b_b-b_t}\\right)z with :math:`t` and :math:`b` subscripts now representing 'top' and 'bottom' of the profile respectively. Using the product rule for differentiation the above integral can be split into: .. math:: \\mathbf{A}_{i}=\\int_{0}^1{\\frac{da\\left(z\\right)}{dz}\\frac{db\\left(z\\right)}{dz}\\phi_i\\,dz} + \\int_{0}^1{a\\left(z\\right)\\frac{d^2b\\left(z\\right)}{dz^2}\\phi_i\\,dz} The right hand term is zero because :math:`b\\left(z\\right)` is a continuous linear function so it's second derivative is zero. The first derivative of :math:`b\\left(z\\right)` is a constant so the left term can be integrated by parts to give: .. math:: \\mathbf{A}_{i}=\\frac{db\\left(z\\right)}{dz}\\left( \\left.\\phi_i{a\\left(z\\right)}\\right|_{z=0}^{z=1} - -\\int_{0}^1{{a\\left(z\\right)}\\frac{d\\phi_i}{dz}\\,dz} \\right) """ v = SympyVarsFor1DSpectralDerivation('z') integ_kwargs = dict(risch=False, conds='none') phi_i = sympy.sin(v.mi * v.z) fcol = - sympy.diff(v.b, v.z) * sympy.integrate(sympy.diff(phi_i, v.z) * v.a, v.z, **integ_kwargs) fcol_loops = fcol.subs(v.z, v.zbot) - fcol.subs(v.z, v.ztop) fcol_loops = fcol_loops.subs(v.map_to_add_index) fcol_vector = fcol.subs(v.z, v.zbot) - fcol.subs(v.z, v.ztop) fcol_vector = fcol_vector.subs(v.map_top_to_t_bot_to_b) v = SympyVarsFor1DSpectralDerivation('z', slope=False) fend = sympy.diff(v.b, v.z) * phi_i * v.a fbot = fend.subs(v.z, v.zbot) fbot_loops = fbot.subs(v.map_to_add_index) nlayers = sympy.tensor.Idx('nlayers') fbot_loops = fbot_loops.subs([(v.layer, nlayers - 1)]) fbot_vector = fbot.subs(v.map_to_add_index[2:]) fbot_vector = fbot_vector.subs([(v.layer, -1)]) ftop = fend.subs(v.z, v.ztop) ftop_loops = ftop.subs(v.map_to_add_index) ftop_loops = ftop_loops.subs([(v.layer, 0)]) ftop_vector = ftop.subs(v.map_to_add_index[2:]) ftop_vector = ftop_vector.subs([(v.layer, 0)]) fends_loops = fbot_loops - ftop_loops fends_vector = fbot_vector - ftop_vector text_python = """def dim1sin_D_aDb_linear(m, at, ab, bt, bb, zt, zb, implementation='vectorized'): #import numpy as np #import this at module level #import math #import this at module level m = np.asarray(m) at = np.asarray(at) ab = np.asarray(ab) bt = np.asarray(bt) bb = np.asarray(bb) zt = np.asarray(zt) zb = np.asarray(zb) neig = len(m) if implementation == 'scalar': sin = math.sin cos = math.cos A = np.zeros(neig, float) nlayers = len(zt) for layer in range(nlayers): a_slope = (ab[layer] - at[layer]) / (zb[layer] - zt[layer]) b_slope = (bb[layer] - bt[layer]) / (zb[layer] - zt[layer]) for i in range(neig): A[i] += ({0}) for i in range(neig): A[i] += ({1}) elif implementation == 'fortran': if MUST_TRY_FORTRAN: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_d_adb_linear(m, at, ab, bt, bb, zt, zb) else: try: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_d_adb_linear(m, at, ab, bt, bb, zt, zb) except ImportError: A = dim1sin_D_aDb_linear(m, at, ab, bt, bb, zt, zb, implementation='vectorized') else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos A = np.zeros(neig, float) a_slope = (ab - at) / (zb - zt) b_slope = (bb - bt) / (zb - zt) mi = m[:, np.newaxis] A[:] = np.sum({2}, axis=1) mi = m A[:]+= ({3}) return A""" # note the the i=j part in the fortran loop below is because # I changed the loop order from layer, i,j to layer, j,i which is # i think faster as first index of a fortran array loops faster # my sympy code is mased on m[i], hence the need for i=j. text_fortran = """\ SUBROUTINE dim1sin_D_aDb_linear(m, at, ab, bt, bb, zt, zb, & a, neig, nlayers) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nlayers REAL(DP), intent(in), dimension(0:neig-1) ::m REAL(DP), intent(in), dimension(0:nlayers-1) :: at,ab,bt,bb,zt,zb REAL(DP), intent(out), dimension(0:neig-1) :: a INTEGER :: i, layer REAL(DP) :: a_slope, b_slope a=0.0D0 DO layer = 0, nlayers-1 a_slope = (ab(layer) - at(layer)) / (zb(layer) - zt(layer)) b_slope = (bb(layer) - bt(layer)) / (zb(layer) - zt(layer)) DO i = 0, neig-1 {0} END DO END DO DO i = 0, neig-1 {1} END DO END SUBROUTINE""" fn = text_python.format(tw(fcol_loops,5), tw(fends_loops,4), tw(fcol_vector,3), tw(fends_vector,3)) fn2 = text_fortran.format(fcode_one_large_expr(fcol_loops, prepend='a(i) = a(i) + '), fcode_one_large_expr(fends_loops, prepend='a(i) = a(i) + ')) return fn, fn2
[docs]def dim1sin_a_linear_between(): """Code generation for Integrations of `sin(mi * z) * a(z)` between [z1, z2] where a(z) is a piecewise linear functions of z. Calculates array A[len(z), len(m)]. Paste the resulting code into `dim1sin_a_linear_between`. Returns ------- fn : string Python code with scalar (loops) implementation. See Also -------- geotecha.speccon.integrals.dim1sin_a_linear_between : Resulting function. geotecha.speccon.integrals.pdim1sin_a_linear_between : Resulting function with PolyLine inputs. Notes ----- The `dim1sin_a_linear_between`, :math:`A`, is given by: .. math:: \\mathbf{A}_{i,j}= \\int_{z_1}^{z_2}{{a\\left(z\\right)}\\phi_j\\,dz} where the basis function :math:`\\phi_j` is given by: .. math:: \\phi_j\\left(z\\right)=\\sin\\left({m_j}z\\right) and :math:`a\\left(z\\right)` is a piecewise linear functions with respect to :math:`z`, that within a layer are defined by: .. math:: a\\left(z\\right) = a_t+\\frac{a_b-a_t}{z_b-z_t}\\left(z-z_t\\right) with :math:`t` and :math:`b` subscripts representing 'top' and 'bottom' of each layer respectively. """ v = SympyVarsFor1DSpectralDerivation('z', slope=True) integ_kwargs = dict(risch=False, conds='none') v.z1 = sympy.tensor.IndexedBase('z1') v.z2 = sympy.tensor.IndexedBase('z2') phi_j = sympy.sin(v.mj * v.z) f = sympy.integrate(v.a * phi_j, v.z, **integ_kwargs) both = f.subs(v.z, v.z2[v.i]) - f.subs(v.z, v.z1[v.i]) both = both.subs(v.map_to_add_index) between = f.subs(v.z, v.zbot) - f.subs(v.z, v.ztop) between = between.subs(v.map_to_add_index) z1_only = f.subs(v.z, v.zbot) - f.subs(v.z, v.z1[v.i]) z1_only = z1_only.subs(v.map_to_add_index) z2_only = f.subs(v.z, v.z2[v.i]) - f.subs(v.z, v.ztop) z2_only = z2_only.subs(v.map_to_add_index) text = """dim1sin_a_linear_between(m, at, ab, zt, zb, z): #import numpy as np #import this globally #import math #import this globally sin=math.sin cos=math.cos m = np.asarray(m) at = np.asarray(at) ab = np.asarray(ab) zt = np.asarray(zt) zb = np.asarray(zb) z = np.atleast_2d(z) z1 = z[:,0] z2 = z[:,1] z_for_interp = np.zeros(len(zt)+1) z_for_interp[:-1] = zt[:] z_for_interp[-1]=zb[-1] (segment_both, segment_z1_only, segment_z2_only, segments_between) = segments_between_xi_and_xj(z_for_interp, z1, z2) nz = len(z) neig = len(m) A = np.zeros((nz,neig), dtype=float) for i in range(nz): for layer in segment_both[i]: a_slope = (ab[layer] - at[layer]) / (zb[layer] - zt[layer]) for j in range(neig): A[i,j] += ({0}) for layer in segment_z1_only[i]: a_slope = (ab[layer] - at[layer]) / (zb[layer] - zt[layer]) for j in range(neig): A[i,j] += ({1}) for layer in segments_between[i]: a_slope = (ab[layer] - at[layer]) / (zb[layer] - zt[layer]) for j in range(neig): A[i,j] += ({2}) for layer in segment_z2_only[i]: a_slope = (ab[layer] - at[layer]) / (zb[layer] - zt[layer]) for j in range(neig): A[i,j] += ({3}) return A""" fn = text.format(tw(both,5), tw(z1_only,5), tw(between,5), tw(z2_only,5)) return fn
[docs]def dim1_ab_linear_between(): """Code generation for Integrations of `a(z) * b(z)` between [z1, z2] where a(z) is a piecewise linear functions of z. Calculates array A[len(z)] Paste the resulting code (at least the loops) into `piecewise_linear_1d.integrate_x1a_x2a_y1a_y2a_multiply_x1b_x2b_y1b_y2b_between`. Returns ------- fn : string Python code with scalar (loops) implementation. See Also -------- geotecha.piecewise.piecewise_linear_1d.integrate_x1a_x2a_y1a_y2a_multiply_x1b_x2b_y1b_y2b_between : Resulting function. Notes ----- The `dim1sin_ab_linear_between`, :math:`A`, is given by: .. math:: \\mathbf{A}_{i}=\\int_{z_1}^{z_2}{{a\\left(z\\right)}{b\\left(z\\right)}\\,dz} where :math:`a\\left(z\\right)` and :math:`b\\left(z\\right)` are piecewise linear functions with respect to :math:`z`, that within a layer are defined by: .. math:: a\\left(z\\right) = a_t+\\frac{a_b-a_t}{z_b-z_t}\\left(z-z_t\\right) with :math:`t` and :math:`b` subscripts representing 'top' and 'bottom' of each layer respectively. """ # Because this integration goes into # geotecha.piecewise.piecewise_linear_1d rather than # speccon.integrals I have had to use a separate map function to massage # the variable names into a naming convention consistent with # piecewise_linear_1d (this is m2 below). # As such don't base anything off this funciton unless you know what # you are doing. v = SympyVarsFor1DSpectralDerivation('z', slope=True) integ_kwargs = dict(risch=False, conds='none') v.z1 = sympy.tensor.IndexedBase('z1') v.z2 = sympy.tensor.IndexedBase('z2') seg = sympy.tensor.Idx('seg') x1a = sympy.tensor.IndexedBase('x1a') x2a = sympy.tensor.IndexedBase('x2a') y1a = sympy.tensor.IndexedBase('y1a') y2a = sympy.tensor.IndexedBase('y2a') y1b = sympy.tensor.IndexedBase('y1b') y2b = sympy.tensor.IndexedBase('y2b') xi = sympy.tensor.IndexedBase('xi') xj = sympy.tensor.IndexedBase('xj') mp2 = [(v.zb[v.layer], x2a[seg]), (v.zt[v.layer], x1a[seg]), (v.ab[v.layer], y2a[seg]), (v.at[v.layer], y1a[seg]), (v.bb[v.layer], y2b[seg]), (v.bt[v.layer], y1b[seg]), (v.z1[v.i], xi[v.i]), (v.z2[v.i], xj[v.i])] f = sympy.integrate(v.a * v.b, v.z, **integ_kwargs) both = f.subs(v.z, v.z2[v.i]) - f.subs(v.z, v.z1[v.i]) both = both.subs(v.map_to_add_index).subs(mp2) between = f.subs(v.z, v.zbot) - f.subs(v.z, v.ztop) between = between.subs(v.map_to_add_index).subs(mp2) z1_only = f.subs(v.z, v.zbot) - f.subs(v.z, v.z1[v.i]) z1_only = z1_only.subs(v.map_to_add_index).subs(mp2) z2_only = f.subs(v.z, v.z2[v.i]) - f.subs(v.z, v.ztop) z2_only = z2_only.subs(v.map_to_add_index).subs(mp2) ###################### # mp, p = create_layer_sympy_var_and_maps(layer_prop=['z', 'a', 'b']) # sympy.var('z1, z2') # # z1 = sympy.tensor.IndexedBase('z1') # z2 = sympy.tensor.IndexedBase('z2') # i = sympy.tensor.Idx('i') # j = sympy.tensor.Idx('j') # # # sympy.var('x1a, x2a, y1a, y2a, x1b, x2b, y1b, y2b') # seg = sympy.tensor.Idx('seg') # x1a = sympy.tensor.IndexedBase('x1a') # x2a = sympy.tensor.IndexedBase('x2a') # y1a = sympy.tensor.IndexedBase('y1a') # y2a = sympy.tensor.IndexedBase('y2a') # y1b = sympy.tensor.IndexedBase('y1b') # y2b = sympy.tensor.IndexedBase('y2b') # xi = sympy.tensor.IndexedBase('xi') # xj = sympy.tensor.IndexedBase('xj') # #mp2 = {'zb[layer]': x2a[seg]} # mp2 = [(mp['zbot'], x2a[seg]), # (mp['ztop'], x1a[seg]), # (mp['abot'], y2a[seg]), # (mp['atop'], y1a[seg]), # (mp['bbot'], y2b[seg]), # (mp['btop'], y1b[seg]), # (z1[i], xi[i]), # (z2[i], xj[i])] # #phi_j = sympy.sin(mj * z) # # f = sympy.integrate(p['a'] * p['b'], z) # # both = f.subs(z, z2[i]) - f.subs(z, z1[i]) # both = both.subs(mp).subs(mp2) # # between = f.subs(z, mp['zbot']) - f.subs(z, mp['ztop']) # between = between.subs(mp).subs(mp2) # # z1_only = f.subs(z, mp['zbot']) - f.subs(z, z1[i]) # z1_only = z1_only.subs(mp).subs(mp2) # # z2_only = f.subs(z, z2[i]) - f.subs(z, mp['ztop']) # z2_only = z2_only.subs(mp).subs(mp2) # text = """A = np.zeros(len(xi)) # for i in range(len(xi)): # for seg in segment_both[i]: # A[i] += {} # for seg in segment_xi_only[i]: # A[i] += {} # for seg in segments_between[i]: # A[i] += {} # for seg in segment_xj_only[i]: # A[i] += {} # # return A""" text = """integrate_x1a_x2a_y1a_y2a_multiply_x1b_x2b_y1b_y2b_between(x1a, x2a, y1a, y2a, x1b, x2b, y1b, y2b, xi, xj): x1a = np.asarray(x1a) x2a = np.asarray(x2a) y1a = np.asarray(y1a) y2a = np.asarray(y2a) x1b = np.asarray(x1b) x2b = np.asarray(x2b) y1b = np.asarray(y1b) y2b = np.asarray(y2b) if (not np.allclose(x1a, x1b)) or (not np.allclose(x2a, x2b)): #they may be different sizes raise ValueError ("x values are different; they must be the same: \\nx1a = {{0}}\\nx1b = {{1}}\\nx2a = {{2}}\\nx2b = {{3}}".format(x1a,x1b, x2a, x2b)) #sys.exit(0) xi = np.atleast_1d(xi) xj = np.atleast_1d(xj) x_for_interp = np.zeros(len(x1a)+1) x_for_interp[:-1] = x1a[:] x_for_interp[-1] = x2a[-1] (segment_both, segment_xi_only, segment_xj_only, segments_between) = segments_between_xi_and_xj(x_for_interp, xi, xj) A = np.zeros(len(xi)) for i in range(len(xi)): for seg in segment_both[i]: a_slope = (y2a[seg] - y1a[seg]) / (x2a[seg] - x1a[seg]) b_slope = (y2b[seg] - y1b[seg]) / (x2b[seg] - x1b[seg]) A[i] += ({0}) for seg in segment_xi_only[i]: a_slope = (y2a[seg] - y1a[seg]) / (x2a[seg] - x1a[seg]) b_slope = (y2b[seg] - y1b[seg]) / (x2b[seg] - x1b[seg]) A[i] += ({1}) for seg in segments_between[i]: a_slope = (y2a[seg] - y1a[seg]) / (x2a[seg] - x1a[seg]) b_slope = (y2b[seg] - y1b[seg]) / (x2b[seg] - x1b[seg]) A[i] += ({2}) for seg in segment_xj_only[i]: a_slope = (y2a[seg] - y1a[seg]) / (x2a[seg] - x1a[seg]) b_slope = (y2b[seg] - y1b[seg]) / (x2b[seg] - x1b[seg]) A[i] += ({3}) return A """ fn = text.format(tw(both,4), tw(z1_only,4), tw(between,4), tw(z2_only,4)) return fn
[docs]def dim1sin_DD_abDDf_linear_implementations(): """Code generation for Integration of sin(mi * z) * D[a(z) * b(z) D[sin(mj * z),z,2],z,2] between ztop and zbot where a(z) and b(z) is piecewise linear functions of z. Code is generated that will produce a square array with the appropriate integrals at each location. Paste the resulting code (at least the loops) into `dim1sin_abf_linear`. Creates three implementations: - 'scalar', python loops (slowest). - 'vectorized', numpy (much faster than scalar). - 'fortran', fortran loops (fastest). Needs to be compiled and interfaced with f2py. Returns ------- fn : string Python code with scalar (loops) and vectorized (numpy) implementations also calls the fortran version. fn2 : string Fortran code. Needs to be compiled with f2py. See Also -------- geotecha.speccon.integrals.dim1sin_DD_abDDf_linear : Resulting function. geotecha.speccon.integrals.pdim1sin_DD_abDDf_linear : Resulting function with PolyLine inputs. geotecha.speccon.ext_integrals.dim1sin_dd_abDddf_linear : Resulting fortran function. Notes ----- the resulting code will produce matrixes of complex values. The `dim1sin_DD_abDDf_linear` matrix, :math:`A` is given by: .. math:: \\mathbf{A}_{i,j}=\\int_{0}^1{\\frac{d^2}{dz^2}\\left({a\\left(z\\right)}{b\\left(z\\right)}\\frac{d^2\\phi_j}{dz^2}\\right)\\phi_i\\,dz} where the basis function :math:`\\phi_i` is given by: .. math:: \\phi_i\\left(z\\right)=\\sin\\left({m_i}z\\right) and :math:`a\\left(z\\right)` and :math:`b\\left(z\\right)` are piecewise linear functions with respect to :math:`z`, that within a layer is defined by: .. math:: a\\left(z\\right) = a_t+\\frac{a_b-a_t}{z_b-z_t}\\left(z-z_t\\right) with :math:`t` and :math:`b` subscripts representing 'top' and 'bottom' of each layer respectively. To make the above integration simpler we integate by parts to get: .. math:: \\mathbf{A}_{i,j}= \\left.{\\frac{d}{dz}\\left({a\\left(z\\right)}{b\\left(z\\right)}\\frac{d^2\\phi_j}{dz^2}\\right)\\phi_i}\\right|_{z=0}^{z=1} - \\left.{{a\\left(z\\right)}{b\\left(z\\right)}\\frac{d^2\\phi_j}{dz^2}\\frac{d\\phi_i}{dz}}\\right|_{z=0}^{z=1} +\\int_{0}^1{{a\\left(z\\right)}{b\\left(z\\right)}\\frac{d^2\\phi_j}{dz^2}\\frac{d^2\\phi_i}{dz^2}\\,dz} In this case the sine basis functions means the end point terms in the above equation are zero, leaving us with .. math:: \\mathbf{A}_{i,j}= \\int_{0}^1{{a\\left(z\\right)}{b\\left(z\\right)}\\frac{d^2\\phi_j}{dz^2}\\frac{d^2\\phi_i}{dz^2}\\,dz} """ # NOTE: remember that fortran does not distinguish between upper and lower # case. When f2py wraps a fortran function with upper case letters then # upper case letters will be converted to lower case. e.g. Therefore when # calling a fortran function called if fortran fn is # 'dim1sin_DD_abDDf_linear' f2py will wrap it as 'dim1sin_dd_abddf_linear' v = SympyVarsFor1DSpectralDerivation('z') integ_kwargs = dict(risch=False, conds='none') phi_i = sympy.sin(v.mi * v.z) phi_j = sympy.sin(v.mj * v.z) fdiag = sympy.integrate(sympy.diff(phi_i, v.z,2) * v.a * v.b * sympy.diff(phi_i, v.z,2), v.z, **integ_kwargs) fdiag_loops = fdiag.subs(v.z, v.zbot) - fdiag.subs(v.z, v.ztop) fdiag_loops = fdiag_loops.subs(v.map_to_add_index) fdiag_vector = fdiag.subs(v.z, v.zbot) - fdiag.subs(v.z, v.ztop) fdiag_vector = fdiag_vector.subs(v.map_top_to_t_bot_to_b) foff = sympy.integrate(sympy.diff(phi_i, v.z, 2) * v.a * v.b * sympy.diff(phi_j, v.z,2), v.z, **integ_kwargs) foff_loops = foff.subs(v.z, v.zbot) - foff.subs(v.z, v.ztop) foff_loops = foff_loops.subs(v.map_to_add_index) foff_vector = foff.subs(v.z, v.zbot) - foff.subs(v.z, v.ztop) foff_vector = foff_vector.subs(v.map_top_to_t_bot_to_b) text_python = """def dim1sin_DD_abDDf_linear(m, at, ab, bt, bb, zt, zb, implementation='vectorized'): #import numpy as np #import this at module level #import math #import this at module level m = np.asarray(m) at = np.asarray(at) ab = np.asarray(ab) bt = np.asarray(bt) bb = np.asarray(bb) zt = np.asarray(zt) zb = np.asarray(zb) neig = len(m) if implementation == 'scalar': sin = math.sin cos = math.cos A = np.zeros([neig, neig], float) nlayers = len(zt) for layer in range(nlayers): a_slope = (ab[layer] - at[layer]) / (zb[layer] - zt[layer]) b_slope = (bb[layer] - bt[layer]) / (zb[layer] - zt[layer]) for i in range(neig): A[i, i] += ({0}) for i in range(neig-1): for j in range(i + 1, neig): A[i, j] += ({1}) #A is symmetric for i in range(neig - 1): for j in range(i + 1, neig): A[j, i] = A[i, j] elif implementation == 'fortran': if MUST_TRY_FORTRAN: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_dd_abddf_linear(m, at, ab, bt, bb, zt, zb) else: try: import geotecha.speccon.ext_integrals as ext_integ A = ext_integ.dim1sin_dd_abddf_linear(m, at, ab, bt, bb, zt, zb) except ImportError: A = dim1sin_DD_abDDf_linear(m, at, ab, bt, bb, zt, zb, implementation='vectorized') else:#default is 'vectorized' using numpy sin = np.sin cos = np.cos A = np.zeros([neig, neig], float) diag = np.diag_indices(neig) triu = np.triu_indices(neig, k = 1) tril = (triu[1], triu[0]) a_slope = (ab - at) / (zb - zt) b_slope = (bb - bt) / (zb - zt) mi = m[:, np.newaxis] A[diag] = np.sum({2}, axis=1) mi = m[triu[0]][:, np.newaxis] mj = m[triu[1]][:, np.newaxis] A[triu] = np.sum({3}, axis=1) #A is symmetric A[tril] = A[triu] return A""" # note the the i=j part in the fortran loop below is because # I changed the loop order from layer, i,j to layer, j,i which is # i think faster as first index of a fortran array loops faster # my sympy code is mased on m[i], hence the need for i=j. text_fortran = """\ SUBROUTINE dim1sin_dd_abddf_linear(m, at, ab, bt, bb, zt, zb, a, & neig, nlayers) USE types IMPLICIT NONE INTEGER, intent(in) :: neig INTEGER, intent(in) :: nlayers REAL(DP), intent(in), dimension(0:neig-1) ::m REAL(DP), intent(in), dimension(0:nlayers-1) :: at,ab,bt,bb,zt,zb REAL(DP), intent(out), dimension(0:neig-1, 0:neig-1) :: a INTEGER :: i , j, layer REAL(DP) :: a_slope, b_slope a=0.0D0 DO layer = 0, nlayers-1 a_slope = (ab(layer) - at(layer)) / (zb(layer) - zt(layer)) b_slope = (bb(layer) - bt(layer)) / (zb(layer) - zt(layer)) DO j = 0, neig-1 i=j {0} DO i = j+1, neig-1 {1} END DO END DO END DO DO j = 0, neig -2 DO i = j + 1, neig-1 a(j,i) = a(i, j) END DO END DO END SUBROUTINE""" fn = text_python.format(tw(fdiag_loops,5), tw(foff_loops,6), tw(fdiag_vector,3), tw(foff_vector,3)) fn2 = text_fortran.format(fcode_one_large_expr(fdiag_loops, prepend='a(i, i) = a(i, i) + '), fcode_one_large_expr(foff_loops, prepend='a(i, j) = a(i, j) + ')) return fn, fn2
if __name__ == '__main__': pass import nose # nose.runmodule(argv=['nose', '--verbosity=3', '--with-doctest']) # nose.runmodule(argv=['nose', '--verbosity=3']) # fn, fn2=Eload_linear_implementations();print(fn);print('#'*40); print(fn2) # fn, fn2=EDload_linear_implementations();print(fn);print('#'*40); print(fn2) # fn, fn2=Eload_coslinear_implementations();print(fn);print('#'*40); print(fn2) # fn, fn2=EDload_coslinear_implementations();print(fn);print('#'*40); print(fn2) # fn, fn2=dim1sin_af_linear_implementations();print(fn);print('#'*40); print(fn2) # fn, fn2=dim1sin_abf_linear_implementations();print(fn);print('#'*40); print(fn2) # fn, fn2=dim1sin_D_aDf_linear_implementations();print(fn);print('#'*40); print(fn2) # fn, fn2=dim1sin_ab_linear_implementations();print(fn);print('#'*40); print(fn2) # fn, fn2=dim1sin_abc_linear_implementations();print(fn);print('#'*40); print(fn2) # fn, fn2=dim1sin_D_aDb_linear_implementations();print(fn);print('#'*40); print(fn2) # fn, fn2=dim1sin_DD_abDDf_linear_implementations();print(fn);print('#'*40); print(fn2) fn, fn2=Eload_sinlinear_implementations();print(fn);print('#'*40); print(fn2) # print(dim1sin_a_linear_between()) # print(dim1_ab_linear_between()) with open("C:\\temp\\temp_py.txt", 'w') as f: f.write(fn) with open("C:\\temp\\temp_for.txt", 'w') as f: f.write(fn2)