Commit ·
2ed2c55
0
Parent(s):
Add space-time simulator Python script
Browse files
py_new.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
from typing import Tuple, Union
|
| 3 |
+
|
| 4 |
+
# Simulate your friend's typing_ and backend modules
|
| 5 |
+
# In your real code, replace these with your actual imports.
|
| 6 |
+
Number = Union[float, int]
|
| 7 |
+
Tensorlike = np.ndarray
|
| 8 |
+
bd = np
|
| 9 |
+
|
| 10 |
+
# Constants (if needed)
|
| 11 |
+
# from . import constants as const
|
| 12 |
+
# For now we won't define constants as none were used explicitly.
|
| 13 |
+
|
| 14 |
+
def curl_E(E: Tensorlike) -> Tensorlike:
|
| 15 |
+
"""
|
| 16 |
+
Transforms an E-type field into an H-type field by performing a curl operation.
|
| 17 |
+
E is located on edges (integer gridpoints), result is on faces (half-integer grid points).
|
| 18 |
+
"""
|
| 19 |
+
curl = bd.zeros(E.shape)
|
| 20 |
+
|
| 21 |
+
# Following the original indexing logic
|
| 22 |
+
curl[:, :-1, :, 0] += E[:, 1:, :, 2] - E[:, :-1, :, 2]
|
| 23 |
+
curl[:, :, :-1, 0] -= E[:, :, 1:, 1] - E[:, :, :-1, 1]
|
| 24 |
+
|
| 25 |
+
curl[:, :, :-1, 1] += E[:, :, 1:, 0] - E[:, :, :-1, 0]
|
| 26 |
+
curl[:-1, :, :, 1] -= E[1:, :, :, 2] - E[:-1, :, :, 2]
|
| 27 |
+
|
| 28 |
+
curl[:-1, :, :, 2] += E[1:, :, :, 1] - E[:-1, :, :, 1]
|
| 29 |
+
curl[:, :-1, :, 2] -= E[:, 1:, :, 0] - E[:, :-1, :, 0]
|
| 30 |
+
|
| 31 |
+
return curl
|
| 32 |
+
|
| 33 |
+
def curl_edge_to_face(v: Tensorlike) -> Tensorlike:
|
| 34 |
+
"""
|
| 35 |
+
Compute the curl of a vector field v on edges to get a field on faces.
|
| 36 |
+
"""
|
| 37 |
+
return curl_E(v)
|
| 38 |
+
|
| 39 |
+
def curl_H(H: Tensorlike) -> Tensorlike:
|
| 40 |
+
"""
|
| 41 |
+
Transforms an H-type field (faces) into an E-type field (edges) by performing a curl operation.
|
| 42 |
+
"""
|
| 43 |
+
curl = bd.zeros(H.shape)
|
| 44 |
+
|
| 45 |
+
curl[:, 1:, :, 0] += H[:, 1:, :, 2] - H[:, :-1, :, 2]
|
| 46 |
+
curl[:, :, 1:, 0] -= H[:, :, 1:, 1] - H[:, :, :-1, 1]
|
| 47 |
+
|
| 48 |
+
curl[:, :, 1:, 1] += H[:, :, 1:, 0] - H[:, :, :-1, 0]
|
| 49 |
+
curl[1:, :, :, 1] -= H[1:, :, :, 2] - H[:-1, :, :, 2]
|
| 50 |
+
|
| 51 |
+
curl[1:, :, :, 2] += H[1:, :, :, 1] - H[:-1, :, :, 1]
|
| 52 |
+
curl[:, 1:, :, 2] -= H[:, 1:, :, 0] - H[:, :-1, :, 0]
|
| 53 |
+
|
| 54 |
+
return curl
|
| 55 |
+
|
| 56 |
+
def curl_face_to_edge(A: Tensorlike) -> Tensorlike:
|
| 57 |
+
"""
|
| 58 |
+
Compute the curl of a vector field A on faces, resulting in a field on edges.
|
| 59 |
+
"""
|
| 60 |
+
return curl_H(A)
|
| 61 |
+
|
| 62 |
+
def div(v: Tensorlike) -> Tensorlike:
|
| 63 |
+
"""
|
| 64 |
+
Compute the divergence of a vector field v defined on edges.
|
| 65 |
+
The result is a scalar field on faces.
|
| 66 |
+
"""
|
| 67 |
+
# Based on your your old code
|
| 68 |
+
div_v = bd.zeros((v.shape[0], v.shape[1], v.shape[2], 1))
|
| 69 |
+
|
| 70 |
+
# x-component
|
| 71 |
+
div_v[1:-1, :, :, 0] += (v[1:-1, :, :, 0] - v[:-2, :, :, 0])
|
| 72 |
+
div_v[:-2, :, :, 0] -= (v[1:-1, :, :, 0] - v[2:, :, :, 0])
|
| 73 |
+
|
| 74 |
+
# y-component
|
| 75 |
+
div_v[:, 1:-1, :, 0] += (v[:, 1:-1, :, 1] - v[:, :-2, :, 1])
|
| 76 |
+
div_v[:, :-2, :, 0] -= (v[:, 1:-1, :, 1] - v[:, 2:, :, 1])
|
| 77 |
+
|
| 78 |
+
# z-component
|
| 79 |
+
div_v[:, :, 1:-1, 0] += (v[:, :, 1:-1, 2] - v[:, :, :-2, 2])
|
| 80 |
+
div_v[:, :, :-2, 0] -= (v[:, :, 1:-1, 2] - v[:, :, 2:, 2])
|
| 81 |
+
|
| 82 |
+
return div_v
|
| 83 |
+
|
| 84 |
+
def grad(p: Tensorlike) -> Tensorlike:
|
| 85 |
+
"""
|
| 86 |
+
Compute the gradient of a scalar field p located on grid points.
|
| 87 |
+
p has shape (Nx+1, Ny+1, Nz+1, 1).
|
| 88 |
+
Returns (Nx+1, Ny+1, Nz+1, 3).
|
| 89 |
+
"""
|
| 90 |
+
grad = bd.zeros((p.shape[0], p.shape[1], p.shape[2], 3))
|
| 91 |
+
|
| 92 |
+
grad[:-1, :, :, 0] = (p[1:, :, :, 0] - p[:-1, :, :, 0])
|
| 93 |
+
grad[:, :-1, :, 1] = (p[:, 1:, :, 0] - p[:, :-1, :, 0])
|
| 94 |
+
grad[:, :, :-1, 2] = (p[:, :, 1:, 0] - p[:, :, :-1, 0])
|
| 95 |
+
|
| 96 |
+
return grad
|
| 97 |
+
|
| 98 |
+
############################################
|
| 99 |
+
# Additional functionalities and example
|
| 100 |
+
############################################
|
| 101 |
+
|
| 102 |
+
def laplacian_vector_field(v: Tensorlike) -> Tensorlike:
|
| 103 |
+
"""
|
| 104 |
+
Compute the vector Laplacian Δv using the provided curl and div.
|
| 105 |
+
Δv = ∇(∇·v) - ∇×(∇×v)
|
| 106 |
+
To do this correctly, we might need v and p fields on compatible staggered grids.
|
| 107 |
+
Here we assume the same indexing as your friend's code.
|
| 108 |
+
"""
|
| 109 |
+
# As an example, let's just apply curl and div in a way similar to the previous code
|
| 110 |
+
# Note: The original code doesn't provide dx, dy, dz, we assume uniform spacing of 1.
|
| 111 |
+
div_v = div(v) # scalar field
|
| 112 |
+
grad_div_v = grad(div_v) # vector field
|
| 113 |
+
c = curl_edge_to_face(v) # curl moves edges->faces
|
| 114 |
+
curl_c = curl_face_to_edge(c) # curl moves faces->edges
|
| 115 |
+
return grad_div_v - curl_c # Δv = ∇(∇·v) - ∇×(∇×v)
|
| 116 |
+
|
| 117 |
+
def apply_second_order_operator(v: Tensorlike, k: float = 1.0) -> Tensorlike:
|
| 118 |
+
"""
|
| 119 |
+
Apply d/dt = -k Δ to a vector field v on edges.
|
| 120 |
+
Here Δv = laplacian_vector_field(v).
|
| 121 |
+
"""
|
| 122 |
+
lap_v = laplacian_vector_field(v)
|
| 123 |
+
return -k * lap_v
|
| 124 |
+
|
| 125 |
+
# Boundary conditions
|
| 126 |
+
def apply_dirichlet_condition(field: np.ndarray, value: float = 0.0):
|
| 127 |
+
field[0, :, :, :] = value
|
| 128 |
+
field[-1, :, :, :] = value
|
| 129 |
+
field[:, 0, :, :] = value
|
| 130 |
+
field[:, -1, :, :] = value
|
| 131 |
+
field[:, :, 0, :] = value
|
| 132 |
+
field[:, :, -1, :] = value
|
| 133 |
+
|
| 134 |
+
def apply_neumann_condition(field: np.ndarray):
|
| 135 |
+
field[0, :, :, :] = field[1, :, :, :]
|
| 136 |
+
field[-1, :, :, :] = field[-2, :, :, :]
|
| 137 |
+
field[:, 0, :, :] = field[:, 1, :, :]
|
| 138 |
+
field[:, -1, :, :] = field[:, -2, :, :]
|
| 139 |
+
field[:, :, 0, :] = field[:, :, 1, :]
|
| 140 |
+
field[:, :, -1, :] = field[:, :, -2, :]
|
| 141 |
+
|
| 142 |
+
def apply_periodic_condition(field: np.ndarray):
|
| 143 |
+
field[0, :, :, :] = field[-2, :, :, :]
|
| 144 |
+
field[-1, :, :, :] = field[1, :, :, :]
|
| 145 |
+
field[:, 0, :, :] = field[:, -2, :, :]
|
| 146 |
+
field[:, -1, :, :] = field[:, 1, :, :]
|
| 147 |
+
field[:, :, 0, :] = field[:, :, -2, :]
|
| 148 |
+
field[:, :, -1, :] = field[:, :, 1, :]
|
| 149 |
+
|
| 150 |
+
if __name__ == "__main__":
|
| 151 |
+
# Example usage:
|
| 152 |
+
# Suppose Nx, Ny, Nz are the number of cells in each direction
|
| 153 |
+
# The given code suggests fields on staggered grids. Adjust sizes as needed.
|
| 154 |
+
Nx, Ny, Nz = 20, 20, 20
|
| 155 |
+
v_field = bd.random.randn(Nx, Ny, Nz, 3) # A random vector field
|
| 156 |
+
|
| 157 |
+
# Choose a boundary type
|
| 158 |
+
boundary_type = 'dirichlet' # 'dirichlet', 'neumann', 'periodic'
|
| 159 |
+
dt = 0.1
|
| 160 |
+
k = 1.0
|
| 161 |
+
num_timesteps = 5
|
| 162 |
+
|
| 163 |
+
for _ in range(num_timesteps):
|
| 164 |
+
dv_dt = apply_second_order_operator(v_field, k)
|
| 165 |
+
v_field = v_field + dt * dv_dt
|
| 166 |
+
|
| 167 |
+
# Apply chosen boundary condition
|
| 168 |
+
if boundary_type == 'dirichlet':
|
| 169 |
+
apply_dirichlet_condition(v_field, value=0.0)
|
| 170 |
+
elif boundary_type == 'neumann':
|
| 171 |
+
apply_neumann_condition(v_field)
|
| 172 |
+
elif boundary_type == 'periodic':
|
| 173 |
+
apply_periodic_condition(v_field)
|
| 174 |
+
|
| 175 |
+
print("Final field shape:", v_field.shape)
|
| 176 |
+
print("Simulation complete.")
|