initial upload
This commit is contained in:
231
PyLaGriT/pylagrit/utilities.py
Executable file
231
PyLaGriT/pylagrit/utilities.py
Executable file
@@ -0,0 +1,231 @@
|
||||
import os
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def zone_to_zonn(zonefile):
|
||||
zonnfile = os.path.splitext(zonefile)[0] + ".zonn"
|
||||
with open(zonefile, "r") as fh:
|
||||
fh.readline()
|
||||
lns = fh.readlines()
|
||||
with open(zonnfile, "w") as fout:
|
||||
fout.write("zonn\n")
|
||||
for l in lns:
|
||||
fout.write(l)
|
||||
|
||||
|
||||
def spherical_writeFEHM(node_locations, filename_base, title="default"):
|
||||
"""
|
||||
Create FEHM .stor and .fehmn input files for 1d spherical simulations.
|
||||
Assumes logical 1D structure.
|
||||
:arg node_locations: radial location of nodes
|
||||
:type node_locations: array(float)
|
||||
:arg filename_base: base name for the .stor and .fehmn files
|
||||
:type filename_base: str
|
||||
:arg title: optional title for simulation
|
||||
:type title: str
|
||||
|
||||
Example:
|
||||
from pylagrit import utilities as util
|
||||
import numpy as np
|
||||
nodes = np.linspace(0.0,200.0)
|
||||
filename_base = "test"
|
||||
title = "test sim"
|
||||
# write .stor and .fehmn files
|
||||
util.spherical_writeFEHM(nodes,filename_base,title)
|
||||
"""
|
||||
# stor file header
|
||||
now = datetime.now()
|
||||
print_datetime = now.strftime("%m/%d/%Y %H:%M:%S")
|
||||
header = (
|
||||
"FEHM .stor file generated by PyLaGriT "
|
||||
+ print_datetime
|
||||
+ "\n"
|
||||
+ "title: "
|
||||
+ title
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
# calculate dx, face locations, face areas, volumes, and geometric coefficients
|
||||
dx = spherical_dx(node_locations)
|
||||
faces = spherical_faces(node_locations)
|
||||
area = spherical_areas(faces)
|
||||
volumes = spherical_volumes(node_locations)
|
||||
coeffs = area / dx
|
||||
|
||||
# matrix metadata header info
|
||||
neq = np.size(node_locations)
|
||||
num_coeffs = neq - 1
|
||||
FEHM_mem = (neq - 2) * 3 + 4 + neq + 1
|
||||
num_area_coeff = 1
|
||||
max_connect = 3
|
||||
matrix_params = (num_coeffs, neq, FEHM_mem, num_area_coeff, max_connect)
|
||||
params_header = " " + " ".join(str(s) for s in matrix_params) + "\n"
|
||||
|
||||
# row count
|
||||
row_count = np.empty([neq + 1])
|
||||
start = neq + 1
|
||||
row_count[0] = start
|
||||
row_count[1] = row_count[0] + 2
|
||||
for i in range(2, neq):
|
||||
row_count[i] = row_count[i - 1] + 3
|
||||
row_count[neq] = row_count[neq - 1] + 2
|
||||
|
||||
# row entries
|
||||
row_entries = []
|
||||
row_entries.append(1)
|
||||
row_entries.append(2)
|
||||
for i in range(1, neq - 1):
|
||||
row_entries.append(i)
|
||||
row_entries.append(i + 1)
|
||||
row_entries.append(i + 2)
|
||||
row_entries.append(neq - 1)
|
||||
row_entries.append(neq)
|
||||
|
||||
# index into geometric coefficient matrix
|
||||
coeff_indices = []
|
||||
coeff_indices.append(0)
|
||||
coeff_indices.append(1)
|
||||
for i in range(1, neq - 1):
|
||||
coeff_indices.append(i)
|
||||
coeff_indices.append(0)
|
||||
coeff_indices.append(i + 1)
|
||||
coeff_indices.append(neq - 1)
|
||||
coeff_indices.append(0)
|
||||
# neq + 1 extra 0s for padding
|
||||
for i in range(0, neq + 1):
|
||||
coeff_indices.append(0)
|
||||
|
||||
# index of diagonal entries
|
||||
diagonal_indices = np.empty([neq])
|
||||
diagonal_indices[0] = neq + 2
|
||||
for i in range(1, neq):
|
||||
diagonal_indices[i] = diagonal_indices[i - 1] + 3
|
||||
|
||||
# open stor file, write header and matrix parameters
|
||||
storfile = filename_base + ".stor"
|
||||
sfile = open(storfile, "w")
|
||||
_ = sfile.write(header)
|
||||
_ = sfile.write(params_header)
|
||||
|
||||
# write Voronoi volumes
|
||||
count = 1
|
||||
for vol in volumes:
|
||||
print(" %1.12e" % vol, end="" if count % 5 else "\n", file=sfile)
|
||||
count += 1
|
||||
if (count - 1) % 5:
|
||||
_ = sfile.write("\n")
|
||||
|
||||
# write row counts
|
||||
count = 1
|
||||
for row in row_count:
|
||||
print(" %9d" % row, end="" if count % 5 else "\n", file=sfile)
|
||||
count += 1
|
||||
if (count - 1) % 5:
|
||||
_ = sfile.write("\n")
|
||||
|
||||
# write row entries
|
||||
count = 1
|
||||
for row in row_entries:
|
||||
print(" %9d" % row, end="" if count % 5 else "\n", file=sfile)
|
||||
count += 1
|
||||
if (count - 1) % 5:
|
||||
_ = sfile.write("\n")
|
||||
|
||||
# write geometric coefficient indices
|
||||
count = 1
|
||||
for idx in coeff_indices:
|
||||
print(" %9d" % idx, end="" if count % 5 else "\n", file=sfile)
|
||||
count += 1
|
||||
if (count - 1) % 5:
|
||||
_ = sfile.write("\n")
|
||||
|
||||
# write diagonal indices
|
||||
count = 1
|
||||
for idx in diagonal_indices:
|
||||
print(" %9d" % idx, end="" if count % 5 else "\n", file=sfile)
|
||||
count += 1
|
||||
if (count - 1) % 5:
|
||||
_ = sfile.write("\n")
|
||||
|
||||
# write geometric coefficients
|
||||
count = 1
|
||||
for coef in coeffs:
|
||||
print(" %1.12e" % coef, end="" if count % 5 else "\n", file=sfile)
|
||||
count += 1
|
||||
|
||||
# close stor file
|
||||
if (count - 1) % 5:
|
||||
_ = sfile.write("\n")
|
||||
_ = sfile.close()
|
||||
|
||||
# open fehmn file
|
||||
fehmnfile = filename_base + ".fehmn"
|
||||
ifile = open(fehmnfile, "w")
|
||||
# write header and neq
|
||||
_ = ifile.write("coor\n%d\n" % neq)
|
||||
# write node number and radial location
|
||||
for i in range(1, neq + 1):
|
||||
print(
|
||||
" %3d %12f 0 0" % (i, node_locations[i - 1]),
|
||||
file=ifile,
|
||||
)
|
||||
# write connectivity
|
||||
_ = ifile.write("\nelem\n")
|
||||
_ = ifile.write("%d %d\n" % (2, neq - 1))
|
||||
for i in range(1, neq):
|
||||
print("%3d %3d %3d" % (i, i, i + 1), file=ifile)
|
||||
# close fehmn file
|
||||
_ = ifile.write("\nstop\n")
|
||||
_ = ifile.close()
|
||||
|
||||
|
||||
def spherical_faces(node_locations):
|
||||
"""
|
||||
Calculate radial interface locations given radial node locations.
|
||||
Assumes 1st and last nodes lie on domain boundary.
|
||||
:arg node_locations: radial location of nodes
|
||||
:type node_locations: array_like(float)
|
||||
Returns: array of radial interface locations of size node_locations - 1
|
||||
"""
|
||||
interface_locations = node_locations[:-1] + np.diff(node_locations) / 2.0
|
||||
return interface_locations
|
||||
|
||||
|
||||
def spherical_areas(interface_locations):
|
||||
"""
|
||||
Calculate spherical area of each interface given radial interface locations.
|
||||
:arg interface_locations: radial location of interfaces
|
||||
:type interface_locations: array_like(float)
|
||||
Returns: array of spherical interface areas of size interface_locations
|
||||
"""
|
||||
areas = 4.0 * np.pi * interface_locations ** 2
|
||||
return areas
|
||||
|
||||
|
||||
def spherical_dx(node_locations):
|
||||
"""
|
||||
Calculate Delaunay edge lengths given radial node locations.
|
||||
:arg node_locations: radial location of nodes
|
||||
:type node_locations: array_like(float)
|
||||
Returns: array of Delaunay edge lengths of size node_locations - 1
|
||||
"""
|
||||
delaunay = np.diff(node_locations)
|
||||
return delaunay
|
||||
|
||||
|
||||
def spherical_volumes(node_locations):
|
||||
"""
|
||||
Calculate Voronoi volume associated with each node given radial node locations.
|
||||
:arg node_locations: radial location of nodes
|
||||
:type node_locations: array_like(float)
|
||||
Returns: array of Voronoi volumes of size node_locations
|
||||
"""
|
||||
coeff = np.pi * 4.0 / 3.0
|
||||
edges = spherical_faces(node_locations)
|
||||
edges = np.insert(edges, 0, node_locations[0])
|
||||
edges = np.append(edges, node_locations[np.size(node_locations) - 1])
|
||||
spheres = coeff * edges ** 3
|
||||
volumes = np.diff(spheres)
|
||||
assert np.all(volumes > 0), "ERROR: Negative volumes are not good."
|
||||
return volumes
|
||||
Reference in New Issue
Block a user