gray/scripts/gray.py
2024-11-04 12:00:17 +01:00

116 lines
2.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'''
Python interface for GRAY
'''
# output files handling
import numpy as np
import collections
import io
from configparser import ConfigParser
# standard GRAY simulation
from pathlib import Path
# Type of a parsed EQDSK file
Eqdsk = collections.namedtuple('Eqdsk', ['flux', 'boundary', 'limiter', 'q'])
def read_eqdsk(filepath: Path) -> Eqdsk:
'''
Loads a full G-EQDSK file and returns:
R, z, ψ(R, z), q(ρ)
'''
def read(file, n):
'''
Reads n records of a G-EQDSK file
'''
x = np.empty(n)
for i in range(n):
x[i] = float(file.read(16).strip())
return x
def skip(file, n):
'''
Skip n records of a G-EQDSK file
'''
file.seek(file.tell() + 16 * n)
with open(filepath) as file:
file = io.StringIO(''.join(file.read().splitlines()))
# comment string
file.read(48)
# number of (R, z) grid points
_, nr, nz = [int(i) for i in file.read(4 * 3).split()]
# build (R, z) grid
r_dim, z_dim, _, r_left, z_mid = read(file, 5)
r = np.linspace(r_left, r_left + r_dim, nr)
z = np.linspace(z_mid - z_dim/2, z_mid + z_dim/2, nz)
# ψ on the axis/boundary
psi_min, psi_max = read(file, 5)[2:4]
# skip other MHD quantities
skip(file, 5 * 2)
skip(file, nr * 4)
# read flux and safety factor
psi = read(file, nr * nz).reshape(nr, nz)
q = read(file, nr)
# normalise flux
psi = abs(psi - psi_min) / abs(psi_max - psi_min)
# plasma boundary and limiter contour
nbound, nlim = [int(i) for i in file.read(5 * 2).split()]
rbound, zbound = read(file, nbound * 2).reshape(nbound, 2).T
rlim, zlim = read(file, nlim * 2).reshape(nlim, 2).T
return Eqdsk(flux=(r, z, psi),
boundary=(rbound, zbound),
limiter=(rlim, zlim),
q=(np.sqrt(np.linspace(0, 1, nr)), abs(q)))
def read_conf(fname: Path):
'''
Parses gray.ini configuration file
'''
config = ConfigParser()
with open(fname) as ini:
config.read_file(ini)
return config
def read_table(fname: Path):
'''
Loads a GRAY output unit file as a structured numpy array
'''
return np.genfromtxt(fname, skip_header=21, names=True)
def get_limiter(conf: ConfigParser, inputs: Path) -> np.array:
'''
Returns the limiter contour
'''
file = conf['equilibrium']['filenm'].strip('"')
if conf['equilibrium'].get('iequil') == 'EQ_ANALYTICAL':
return np.loadtxt(inputs / file, skiprows=4).T
else:
eqdsk = read_eqdsk(inputs / file)
return eqdsk.limiter
def decode_index_rt(i: int) -> (str, int):
'''
Given the index_rt of a beam, returns the polarisation
mode ('O' or 'X') and the number of passes
'''
mode = ['X', 'O'][i % 2]
passes = int(np.floor(np.log2(1 + i)))
return mode, passes