Source code for andfn.const_head

"""
Notes
-----
This module contains the constant head classes.
"""

from . import math_functions as mf
from . import geometry_functions as gf
import numpy as np
from .element import Element


[docs] class ConstantHeadLine(Element):
[docs] def __init__(self, label, endpoints0, head, frac0, ncoef=5, nint=10, **kwargs): """ Constructor for the constant head line element. Parameters ---------- label : str The label of the constant head line endpoints0 : np.ndarray[complex] The endpoints of the constant head line head : float The head of the constant head line frac0 : Fracture The fracture that the constant head line is associated with ncoef : int The number of coefficients in the asymptotic expansion nint : int The number of integration points in the asymptotic expansion kwargs : dict Additional keyword arguments """ super().__init__(label, id_=0, type_=3) self.label = label self.endpoints0 = endpoints0 self.ncoef = ncoef self.nint = nint self.q = 0.0 self.head = head self.frac0 = frac0 # Create the pre-calculation variables self.thetas = np.linspace( start=np.pi / (2 * nint), stop=np.pi + np.pi / (2 * nint), num=nint, endpoint=False, ) self.coef = np.zeros(ncoef, dtype=complex) self.phi = frac0.phi_from_head(head) # Set the kwargs for key, value in kwargs.items(): setattr(self, key, value) # Assign to the fracture self.frac0.add_element(self)
[docs] def discharge_term(self, z): """ Calculate the discharge term for the constant head line. Parameters ---------- z : np.ndarray The points to calculate the discharge term at Returns ------- float The discharge term """ chi = gf.map_z_line_to_chi(z, self.endpoints0) return np.sum(np.real(mf.well_chi(chi, 1))) / len(z)
[docs] def update_head(self, head): """ Update the head of the constant head line. Parameters ---------- head : float The new head of the constant head line-- """ self.head = head self.phi = self.frac0.phi_from_head(head)
[docs] def length(self): """ Calculate the length of the constant head line. Returns ------- float The length of the constant head line """ return np.abs(self.endpoints0[0] - self.endpoints0[1])
[docs] def z_array(self, n): """ Create an array of z points along the constant head line. Parameters ---------- n : int The number of points to create Returns ------- np.ndarray The array of z points """ return np.linspace(self.endpoints0[0], self.endpoints0[1], n + 2)[1 : n + 1]
[docs] def omega_along_element(self, n, frac_is): """ Calculate the omega along the constant head line. Parameters ---------- n : int The number of points to calculate the omega at frac_is : Fracture The fracture that the calculation is being done for Returns ------- np.ndarray The complex discharge potential along the constant head line """ z = self.z_array(n) omega = frac_is.calc_omega(z) return omega
[docs] def z_array_tracking(self, n, offset=1e-3): """ Create an array of z points along the constant head line with an offset. Parameters ---------- n : int The number of points to create offset : float The offset to use Returns ------- np.ndarray The array of z points """ chi = np.exp(1j * np.linspace(0, 2 * np.pi, n, endpoint=False)) * (1 + offset) return gf.map_chi_to_z_line(chi, self.endpoints0)
[docs] def calc_omega(self, z): """ Calculate the complex discharge potential for the constant head line. Parameters ---------- z : np.ndarray The points to calculate the complex discharge potential at Returns ------- np.ndarray The complex discharge potential """ # Map the z point to the chi plane chi = gf.map_z_line_to_chi(z, self.endpoints0) # Calculate omega omega = mf.asym_expansion(chi, self.coef) + mf.well_chi(chi, self.q) return omega
[docs] def calc_w(self, z): """ Calculate the complex discharge vector for the constant head line. Parameters ---------- z : np.ndarray The points to calculate the complex discharge vector at Returns ------- np.ndarray The complex discharge vector """ # Map the z point to the chi plane chi = gf.map_z_line_to_chi(z, self.endpoints0) # Calculate w w = -mf.asym_expansion_d1(chi, self.coef) - self.q / (2 * np.pi * chi) w *= 2 * chi**2 / (chi**2 - 1) * 2 / (self.endpoints0[1] - self.endpoints0[0]) return w
[docs] def solve(self): """ Solve the coefficients of the constant head line. """ s = mf.cauchy_integral_real( self.nint, self.ncoef, self.thetas, lambda z: self.frac0.calc_omega(z, exclude=self), lambda chi: gf.map_chi_to_z_line(chi, self.endpoints0), ) s = np.real(s) s[0] = ( 0 # Set the first coefficient to zero (constant embedded in discharge matrix) ) self.error = np.max(np.abs(s + self.coef)) self.coef = -s
[docs] def check_boundary_condition(self, n=10): """ Check if the constant head line satisfies the boundary conditions. Parameters ---------- n : int The number of points to check the boundary condition at Returns ------- float The error in the boundary condition """ chi = np.exp(1j * np.linspace(0, np.pi, n)) # Calculate the head in fracture 0 z0 = gf.map_chi_to_z_line(chi, self.endpoints0) omega0 = self.frac0.calc_omega(z0, exclude=None) return np.mean(np.abs(self.phi - np.real(omega0))) / np.abs(self.phi)
[docs] def check_chi_crossing(self, z0, z1, atol=1e-12): """ Check the line between two points crosses the constant head line. Parameters ---------- z0 : complex The first point z1 : complex The second point atol : float The absolute tolerance for the check Returns ------- complex | bool The intersection point if it exists, otherwise False """ z = gf.line_line_intersection(z0, z1, self.endpoints0[0], self.endpoints0[1]) # TODO: implement this if np.abs(np.imag(chi1)) > atol and np.abs(np.imag(chi2)) > atol: if z is None: return False if np.abs(np.abs(z - z0) + np.abs(z1 - z) - np.abs(z0 - z1)) > atol: return False if ( np.abs( np.abs(z - self.endpoints0[0]) + np.abs(z - self.endpoints0[1]) - np.abs(self.endpoints0[0] - self.endpoints0[1]) ) > atol ): return False return z