Coverage for magnetismi/model/cof.py: 99.07%
88 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-04 20:21:25 +00:00
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-04 20:21:25 +00:00
1import importlib.resources
2import math
3import pathlib
5from magnetismi import ENCODING
7CS = ('n', 'm', 'gnm', 'hnm', 'dgnm', 'dhnm')
8STORE_PATH = pathlib.Path('magnetismi', 'model')
9TWIN_END_TOKEN = '9' * 48
11YEARS_COVERED = tuple(y for y in range(2020, 2025 + 1))
12MODEL_FROM_YEAR = {y: '2020' for y in range(2020, 2025 + 1)}
15class Coefficients:
16 """The WMM coefficients relevant to the year given."""
18 model = {} # type: ignore
20 def load(self, model_year: int) -> None:
21 """Load the model."""
22 model_resource = f'wmm-{model_year}.txt'
23 with importlib.resources.path(__package__, model_resource) as model_path:
24 with open(model_path, 'rt', encoding=ENCODING) as handle:
25 recs = [line.strip().split() for line in handle if line.strip() and line.strip() != TWIN_END_TOKEN]
27 epoc, model, model_date_string = recs[0]
28 self.model['epoch'] = float(epoc)
29 self.model['model_code'] = model
30 self.model['model_date_string'] = model_date_string
32 self.model['coeffs'] = [
33 {c: int(r[s]) if c in ('n', 'm') else float(r[s]) for s, c in enumerate(CS)}
34 for r in recs[1:]
35 if len(r) == len(CS)
36 ]
38 def _zeroes(self, count: int) -> list[float]:
39 """Short and sweet."""
40 return [0.0 for _ in range(count)]
42 def evaluate(self) -> None:
43 """Ja, ja, ja!"""
44 self.maxord = self.maxdeg = 12
45 self.tc = [self._zeroes(13) for _ in range(14)]
46 self.sp = self._zeroes(14)
47 self.cp = self._zeroes(14)
48 self.cp[0] = 1.0
49 self.pp = self._zeroes(13)
50 self.pp[0] = 1.0
51 self.p = [self._zeroes(14) for _ in range(14)]
52 self.p[0][0] = 1.0
53 self.dp = [self._zeroes(13) for _ in range(14)]
54 self.a = 6378.137
55 self.b = 6356.7523142
56 self.re = 6371.2
57 self.a2 = self.a * self.a
58 self.b2 = self.b * self.b
59 self.c2 = self.a2 - self.b2
60 self.a4 = self.a2 * self.a2
61 self.b4 = self.b2 * self.b2
62 self.c4 = self.a4 - self.b4
64 self.c = [self._zeroes(14) for _ in range(14)]
65 self.cd = [self._zeroes(14) for _ in range(14)]
67 for wmmnm in self.model['coeffs']:
68 m = wmmnm['m']
69 n = wmmnm['n']
70 gnm = wmmnm['gnm']
71 hnm = wmmnm['hnm']
72 dgnm = wmmnm['dgnm']
73 dhnm = wmmnm['dhnm']
74 if m <= n: 74 ↛ 67line 74 didn't jump to line 67, because the condition on line 74 was never false
75 self.c[m][n] = gnm
76 self.cd[m][n] = dgnm
77 if m != 0:
78 self.c[n][m - 1] = hnm
79 self.cd[n][m - 1] = dhnm
81 # Convert Schmidt normalized Gauss coefficients to unnormalized
82 self.snorm = [self._zeroes(13) for _ in range(13)]
83 self.snorm[0][0] = 1.0
84 self.k = [self._zeroes(13) for _ in range(13)]
85 self.k[1][1] = 0.0
86 self.fn = [float(n) for n in range(14) if n != 1]
87 self.fm = [float(n) for n in range(13)]
88 for n in range(1, self.maxord + 1):
89 self.snorm[0][n] = self.snorm[0][n - 1] * (2.0 * n - 1) / n
90 j = 2.0
91 # for (m=0,D1=1,D2=(n-m+D1)/D1;D2>0;D2--,m+=D1):
92 m = 0
93 D1 = 1
94 D2 = (n - m + D1) / D1
95 while D2 > 0:
96 self.k[m][n] = (((n - 1) * (n - 1)) - (m * m)) / ((2.0 * n - 1) * (2.0 * n - 3.0))
97 if m > 0:
98 flnmj = ((n - m + 1.0) * j) / (n + m)
99 self.snorm[m][n] = self.snorm[m - 1][n] * math.sqrt(flnmj)
100 j = 1.0
101 self.c[n][m - 1] = self.snorm[m][n] * self.c[n][m - 1]
102 self.cd[n][m - 1] = self.snorm[m][n] * self.cd[n][m - 1]
103 self.c[m][n] = self.snorm[m][n] * self.c[m][n]
104 self.cd[m][n] = self.snorm[m][n] * self.cd[m][n]
105 D2 = D2 - 1
106 m = m + D1
108 def __init__(self, year: int):
109 """Initialize the mode from file in case it covers the requested year."""
110 if year not in YEARS_COVERED:
111 raise ValueError(f'requested year ({year}) not within {YEARS_COVERED}')
113 self.model = {
114 'year_requested': year,
115 'model_id': MODEL_FROM_YEAR[year],
116 }
117 self.load(self.model['model_id'])
118 self.evaluate()