Source code for andfn.hpc.hpc_fracture

"""
Notes
-----
This module contains the HPC fracture functions.
"""

import numpy as np
import numba as nb
from andfn.hpc import (
    hpc_intersection,
    hpc_const_head_line,
    hpc_well,
    hpc_bounding_circle,
    hpc_imp_object,
    CACHE,
)
from andfn.hpc import hpc_geometry_functions as gf


[docs] @nb.njit(cache=CACHE) def sunflower_spiral(n_in, n_bnd): """ Generate n points in a sunflower spiral pattern. Parameters ---------- n_in : int Number of points to generate. n_bnd : int Number of boundary points to add along the unit circle. Returns ------- z : np.ndarray[np.complex128] An array of shape (n,) containing the complex coordinates of the points in the sunflower spiral. """ indices = np.arange(0, n_in, dtype=np.float64) + 0.5 r = np.sqrt(indices / n_in) theta = np.pi * (1 + 5**0.5) * indices # Convert polar coordinates to complex numbers z = r * np.cos(theta) + 1j * r * np.sin(theta) # Add points along the boundary of the unit circle z = np.concatenate((z, np.exp(1j * np.linspace(0, 2 * np.pi, n_bnd)))) return z
[docs] @nb.njit() def calc_omega(self_, z, element_struc_array, exclude=-1): """ Calculates the omega for the fracture excluding element "exclude". Parameters ---------- self_ : np.ndarray[fracture_dtype] The fracture element. z : complex A point in the complex z plane. element_struc_array : np.ndarray[element_dtype] Array of elements. exclude : int Label of element to exclude from the omega calculation. Returns ------- omega : complex The complex potential for the fracture. """ # Initialize omega with the constant value omega = self_["constant"] + 0.0j # Loop through the elements of the fracture for e in range(self_["nelements"]): el = self_["elements"][e] if el != exclude: element = element_struc_array[el] if element["_type"] == 0: # Intersection omega += hpc_intersection.calc_omega(element, z, self_["_id"]) elif element["_type"] == 1: # Bounding circle omega += hpc_bounding_circle.calc_omega(element, z) elif element["_type"] == 2: # Well omega += hpc_well.calc_omega(element, z) elif element["_type"] == 3: # Constant head line omega += hpc_const_head_line.calc_omega(element, z) elif element["_type"] == 4: # Impermeable circle omega += hpc_imp_object.calc_omega_circle(element, z) elif element["_type"] == 5: # Impermeable line omega += hpc_imp_object.calc_omega_line(element, z) return omega
[docs] def calc_w(self_, z, element_struc_array, exclude=-1): """ Calculates the omega for the fracture excluding element "exclude". Parameters ---------- self_ : np.ndarray[fracture_dtype] The fracture element. z : complex A point in the complex z plane. element_struc_array : np.ndarray[element_dtype] Array of elements. exclude : int Label of element to exclude from the omega calculation. Returns ------- w : complex The complex potential for the fracture. """ w = 0.0 + 0.0j for e in range(self_["nelements"]): el = self_["elements"][e] if el != exclude: element = element_struc_array[el] if element["_type"] == 0: # Intersection w += hpc_intersection.calc_w(element, z, self_["_id"]) elif element["_type"] == 1: # Bounding circle w += hpc_bounding_circle.calc_w(element, z) elif element["_type"] == 2: # Well w += hpc_well.calc_w(element, z) elif element["_type"] == 3: # Constant head line w += hpc_const_head_line.calc_w(element, z) return w
[docs] @nb.njit(cache=CACHE) def calc_flow_net(self_, flow_net, n_points, z_array, element_struc_array): """ Calculates the flow net for the fracture. Parameters ---------- self_ : np.ndarray[fracture_dtype] The fracture element. flow_net : np.ndarray[complex] The flow net to be calculated. n_points : int Number of points in the flow net. z_array : np.ndarray[np.complex128] Array of complex coordinates for the points in the sunflower spiral multiplied with the fracture radius. element_struc_array : np.ndarray[element_dtype] Array of elements. Returns ------- flow_net : np.ndarray[complex] The flow net for the fracture. """ for i in range(n_points): flow_net[i] = calc_omega(self_, z_array[i], element_struc_array)
[docs] @nb.njit(cache=CACHE) def calc_heads(self_, heads, n_points, z_array, element_struc_array): """ Calculates the head net for the fracture. Parameters ---------- self_ : np.ndarray[fracture_dtype] The fracture element. heads : np.ndarray[np.float64] Array to store the head net for the fracture. n_points : int Number of points in the flow net. z_array : np.ndarray[np.complex128] Array of complex coordinates for the points in the sunflower spiral multiplied with the fracture radius. element_struc_array : np.ndarray[element_dtype] Array of elements. Returns ------- None Modifies the heads array in place. """ # Calculate the head net for the fracture for i in range(n_points): phi = np.real(calc_omega(self_, z_array[i], element_struc_array)) heads[i] = phi / self_["t"]
[docs] @nb.njit(cache=CACHE, parallel=True) def get_flow_nets(fracture_struc_array, n_points, n_boundary_points, element_struc_array): """ Get the flow nets for all fractures. Parameters ---------- fracture_struc_array : np.ndarray[fracture_dtype] The fracture structure array. n_points : int Number of points in the flow net. n_boundary_points : int Number of points along the boundary of the unit circle. element_struc_array : np.ndarray[element_dtype] Array of elements. Returns ------- flow_nets : list[np.ndarray[complex]] List of flow nets for each fracture. """ n = n_points + n_boundary_points # Create the flow nets arrays flow_nets = np.zeros( (len(fracture_struc_array), n, n), dtype=np.complex128 ) # Create the 3D points arrays and its working z arrays pnts_3d = np.zeros((len(fracture_struc_array), n, 3), dtype=np.float64) z_arrays = np.zeros((len(fracture_struc_array), n), dtype=np.complex128) z_array = sunflower_spiral(n_points, n_boundary_points) # Calculate the flow nets for each fracture for i in nb.prange(len(fracture_struc_array)): z_arrays[i] = z_array * fracture_struc_array[i]["radius"] calc_flow_net( fracture_struc_array[i], flow_nets[i], n, z_arrays[i], element_struc_array, ) # Map the 2D points to 3D gf.map_2d_to_3d(fracture_struc_array[i], z_arrays[i], pnts_3d[i]) return flow_nets, pnts_3d
[docs] @nb.njit(cache=CACHE, parallel=True) def get_heads(fracture_struc_array, n_points, n_boundary_points, element_struc_array): """ Get the heads for all fractures. Parameters ---------- fracture_struc_array : np.ndarray[fracture_dtype] The fracture structure array. n_points : int Number of points in the flow net. n_boundary_points : int Number of points along the boundary of the unit circle. element_struc_array : np.ndarray[element_dtype] Array of elements. Returns ------- heads : list[np.ndarray[complex]] List of heads for each fracture. """ n = n_points + n_boundary_points # Create the heads arrays heads = np.zeros((len(fracture_struc_array), n), dtype=np.float64) # Create the 3D points arrays and its working z arrays pnts_3d = np.zeros((len(fracture_struc_array), n, 3), dtype=np.float64) z_arrays = np.zeros((len(fracture_struc_array), n), dtype=np.complex128) z_array = sunflower_spiral(n_points, n_boundary_points) # Calculate the heads for each fracture for i in nb.prange(len(fracture_struc_array)): z_arrays[i] = z_array * fracture_struc_array[i]["radius"] calc_heads( fracture_struc_array[i], heads[i], n, z_arrays[i], element_struc_array, ) # Map the 2D points to 3D gf.map_2d_to_3d(fracture_struc_array[i], z_arrays[i], pnts_3d[i]) return heads, pnts_3d