1#!/usr/local/bin/python3.8
2"""
3Convert polarised CST element files to OSKAR scalar element pattern format.
4"""
5
6from __future__ import print_function
7import sys
8import numpy
9
10def load_cst_file(filename):
11    """"
12    Loads a CST element pattern file into a numpy matrix.
13
14    Parameters
15    ----------
16    filename : string
17        Path of the CST element pattern file to load.
18
19    Returns
20    -------
21    Matrix of values from the CST file.
22    """
23    f = open(filename, 'r')
24    lines = f.readlines()
25    f.close()
26    X = []
27    for line in lines:
28        values = line.split()
29        if not len(values) == 8:
30            continue
31        else:
32            x_all = numpy.array(values, dtype=numpy.dtype('f8'))
33            X.append(x_all)
34    return numpy.array(X, dtype=numpy.dtype('f8'))
35
36
37def convert(cst_file_in, scalar_file_out):
38    """
39    Calculates a scalar element pattern file from a CST element pattern file
40
41    Parameters
42    ----------
43    cst_file_in : string
44        Input CST format element pattern file
45    scalar_file_out : string
46        Output scalar format element pattern file
47
48    Notes
49    -----
50    This function is designed to be used to create scalar element input files
51    for the oskar_fit_element_data application.
52    """
53    # Load the CST element pattern data.
54    X = load_cst_file(cst_file_in)
55    # Only require columns for:
56    # Theta, Phi, Abs(Theta), Phase(Theta), Abs(Phi), Phase(Phi)
57    X = numpy.copy(X[:, [0, 1, 3, 4, 5, 6]])
58
59    # Discard any data at values of phi >= 360 degrees,
60    # as any duplicated entries will cause this method to fail.
61    X = X[X[:, 1] < 360.0, :]
62
63    # Generate the rotated data for Y from X by adding 90 degrees to the phi
64    # values
65    Y = numpy.copy(X)
66    Y[:, 1] += 90.0
67    Y[Y[:, 1] >= 360.0, 1] -= 360.0
68
69    # Linked column sort by phi and then theta for both X and Y.
70    X = X[numpy.lexsort((X[:, 0], X[:, 1])), :]
71    Y = Y[numpy.lexsort((Y[:, 0], Y[:, 1])), :]
72
73    # Check that the coordinate columns in X and Y now match.
74    assert numpy.sum(numpy.abs(X[:, 0] - Y[:, 0])) < 1e-6
75    assert numpy.sum(numpy.abs(X[:, 1] - Y[:, 1])) < 1e-6
76
77    # Generate scalar values from sorted data.
78    X_theta = X[:, 2] * numpy.exp(1j * numpy.radians(X[:, 3]))
79    X_phi = X[:, 4] * numpy.exp(1j * numpy.radians(X[:, 5]))
80    Y_theta = Y[:, 2] * numpy.exp(1j * numpy.radians(Y[:, 3]))
81    Y_phi = Y[:, 4] * numpy.exp(1j * numpy.radians(Y[:, 5]))
82    s = X_theta * numpy.conj(X_theta) + X_phi * numpy.conj(X_phi) + \
83        Y_theta * numpy.conj(Y_theta) + Y_phi * numpy.conj(Y_phi)
84
85    # Take the sqrt to convert to a 'voltage'
86    s = numpy.sqrt(0.5 * s)
87    s_amp = numpy.absolute(s)
88    s_phase = numpy.angle(s, deg=True)
89
90    # Write scalar values to file Columns = (theta, phi, amp, phase).
91    o = numpy.column_stack((X[:, 0], X[:, 1], s_amp, s_phase))
92    numpy.savetxt(scalar_file_out, o,
93                  fmt=['%12.4f', '%12.4f', '%20.6e', '%12.4f'])
94
95if __name__ == "__main__":
96    if len(sys.argv) < 3:
97        print("Usage: oskar_convert_cst_to_scalar.py "
98              "<input CST file> <output scalar file>")
99        sys.exit(1)
100
101    convert(sys.argv[1], sys.argv[2])
102