Skip to content

Commit 88583c8

Browse files
authored
IGRF 14th model added. (#41)
* Updated to IGRF 14th model
1 parent 6d78106 commit 88583c8

File tree

20 files changed

+426
-344
lines changed

20 files changed

+426
-344
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@
1313

1414
# Dependency directories (remove the comment below to include it)
1515
# vendor/
16+
17+
# VS Code
18+
.vscode

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
[![golangci-lint](https://github.com/proway2/go-igrf/actions/workflows/golangci-lint.yml/badge.svg)](https://github.com/proway2/go-igrf/actions/workflows/golangci-lint.yml)
33
[![Go Reference](https://pkg.go.dev/badge/github.com/proway2/go-igrf.svg)](https://pkg.go.dev/github.com/proway2/go-igrf)
44
[![Go Report Card](https://goreportcard.com/badge/github.com/proway2/go-igrf)](https://goreportcard.com/report/github.com/proway2/go-igrf)
5-
[![IGRF badge](https://badgen.net/static/IGRF%20model/13th/green)](https://www.ncei.noaa.gov/products/international-geomagnetic-reference-field)
5+
[![IGRF badge](https://badgen.net/static/IGRF%20model/14th/green)](https://www.ncei.noaa.gov/products/international-geomagnetic-reference-field)
66

77
# go-igrf
88
Pure Go IGRF (International Geomagnetic Reference Field). This is based on the existing `C` implementation. This package computes values for the geomagnetic field and secular variation for a given set of coordinates and date.
@@ -20,9 +20,9 @@ The output is of type `type IGRFresults struct`. Fields are:
2020

2121
|Field name|Fortran name|Unit of measurement|Sample value|Notes|
2222
|-|-|-|-|-|
23-
|Declination|(D)|decimal degrees(°)|14.175 °|(+ve east)|
23+
|Declination|(D)|decimal degrees(°)|14.175|degrees east|
2424
|DeclinationSV|(D)|arcmin/yr|-8.92||
25-
|Inclination|(I)|decimal degrees(°)|74.229|(+ve down)
25+
|Inclination|(I)|decimal degrees(°)|74.229|degrees down|
2626
|InclinationSV|(I)|arcmin/yr|-2.59||
2727
|HzIntensity|Horizontal intensity (H)|nT|14626.6||
2828
|HorizontalSV|(H)|nT/yr|-16.8||
@@ -75,6 +75,7 @@ func main() {
7575

7676
## References
7777

78+
- [IAGA V-MOD WG and main IGRF website](https://www.ncei.noaa.gov/products/international-geomagnetic-reference-field)
7879
- [Alken, P., Thébault, E., Beggan, C.D. et al. International Geomagnetic Reference Field: the thirteenth generation. Earth Planets Space 73, 49 (2021).](https://rdcu.be/cKqZv) https://doi.org/10.1186/s40623-020-01288-x
79-
80+
- [International Association of Geomagnetism and Aeronomy. (2024). IGRF-14. Zenodo.](https://doi.org/10.5281/zenodo.14012302)
8081
- Implementation is based on [geomag70.c](https://www.ngdc.noaa.gov/IAGA/vmod/geomag70_linux.tar.gz). [License information](https://www.ngdc.noaa.gov/IAGA/vmod/geomag70_license.html).

calc/shval3.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import "math"
66
// The calculation is performed for two sets of coeffs for a single location,
77
// thus it returns two sets of X, Y, Z.
88
//
9-
// X - northward component
9+
// # X - northward component
1010
//
11-
// Y - eastward component
11+
// # Y - eastward component
1212
//
13-
// Z - vertically-downward component
13+
// # Z - vertically-downward component
1414
func Shval3(flat, flon, elev float64, nmax int, gha, ghb *[]float64) (float64, float64, float64, float64, float64, float64) {
1515
// similar to shval3 from C implementation
1616
var earths_radius float64 = 6371.2
@@ -157,13 +157,13 @@ func Shval3(flat, flon, elev float64, nmax int, gha, ghb *[]float64) (float64, f
157157

158158
// Computes the geomagnetic D, I, H, and F from X, Y, and Z.
159159
//
160-
// D - declination
160+
// # D - declination
161161
//
162-
// I - inclination
162+
// # I - inclination
163163
//
164-
// H - horizontal intensity
164+
// # H - horizontal intensity
165165
//
166-
// F - total intensity
166+
// # F - total intensity
167167
func Dihf(x, y, z float64) (float64, float64, float64, float64) {
168168
var d, i, h, f float64
169169
sn := 0.0001

coeffs/coeffs.go

Lines changed: 199 additions & 199 deletions
Large diffs are not rendered by default.

coeffs/read.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (igrf *IGRFcoeffs) Coeffs(date float64) (*[]float64, *[]float64, int, error
4949
min_epoch := (*igrf.epochs)[0]
5050
max_epoch := (*igrf.epochs)[max_column-1]
5151
if date < min_epoch || date > max_epoch {
52-
return nil, nil, 0, fmt.Errorf("Date %v is out of range (%v, %v).", date, min_epoch, max_epoch)
52+
return nil, nil, 0, fmt.Errorf("date %v is out of range (%v, %v)", date, min_epoch, max_epoch)
5353
}
5454
// calculate coeffs for the requested date
5555
start, end := igrf.findEpochs(date)
@@ -90,7 +90,7 @@ func (igrf *IGRFcoeffs) interpolateCoeffs(start_epoch, end_epoch string, date fl
9090
} else {
9191
if nmax1 > nmax2 {
9292
// the last column has degree of 8
93-
// now it's anything after 2020.0
93+
// now it's anything after 2025.0
9494
k = nmax2 * (nmax2 + 2)
9595
l = nmax1 * (nmax1 + 2)
9696
interp = func(start, end, f float64) float64 {
@@ -101,7 +101,7 @@ func (igrf *IGRFcoeffs) interpolateCoeffs(start_epoch, end_epoch string, date fl
101101
// between 1995.0 and 2000.0
102102
k = nmax1 * (nmax1 + 2)
103103
l = nmax2 * (nmax2 + 2)
104-
interp = func(start, end, f float64) float64 {
104+
interp = func(_, end, f float64) float64 {
105105
return f * end
106106
}
107107
nmax = nmax2
@@ -212,7 +212,7 @@ func getEpochs(reader <-chan string) (*[]string, *[]float64, error) {
212212
names, epochs := parseHeader(line, line2)
213213
return &names, &epochs, nil
214214
}
215-
return nil, nil, errors.New("Unable to get epochs.")
215+
return nil, nil, errors.New("unable to get epochs")
216216
}
217217

218218
// Parses the header of the coeffs. Usually it's the first two non-comment lines.
@@ -257,7 +257,7 @@ func (igrf *IGRFcoeffs) getCoeffsForEpochs(provider <-chan string) error {
257257
line_data := space_re.Split(line, -1)
258258
line_coeffs, err := parseArrayToFloat(line_data[3:])
259259
if err != nil {
260-
return errors.New("Unable to parse coeffs.")
260+
return errors.New("unable to parse coeffs")
261261
}
262262
igrf.loadCoeffs(i, line_coeffs)
263263
i++
@@ -284,7 +284,7 @@ func nMaxForEpoch(epoch string) (int, error) {
284284
}
285285
if epoch_f < 2000.0 {
286286
nmax = 10
287-
} else if epoch_f > 2020.0 {
287+
} else if epoch_f > 2025.0 {
288288
nmax = 8
289289
} else {
290290
nmax = 13

coeffs/read_test.go

Lines changed: 18 additions & 10 deletions
Large diffs are not rendered by default.

coeffs/utils.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func parseArrayToFloat(raw_data []string) (*[]float64, error) {
9898
for index, token := range raw_data {
9999
real_data, err := strconv.ParseFloat(token, 32)
100100
if err != nil {
101-
return nil, errors.New("Unable to parse coeffs.")
101+
return nil, errors.New("unable to parse coeffs")
102102
}
103103
if index == len(raw_data)-1 {
104104
// real value calculated for the SV column

igrf/igrf.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func New() *IGRFdata {
3434
// date - decimal date (1900.00 to 2025).
3535
func (igd *IGRFdata) IGRF(lat, lon, alt, date float64) (IGRFresults, error) {
3636
if igd.shc == nil {
37-
return IGRFresults{}, errors.New("IGRFdata structure is not initialized.")
37+
return IGRFresults{}, errors.New("IGRFdata structure is not initialized")
3838
}
3939
if err := checkInitialConditions(lat, lon, alt); err != nil {
4040
return IGRFresults{}, err

igrf/igrf_test.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,12 @@ func TestIGRFdata_IGRF(t *testing.T) {
9292
}
9393
// Horizontal intensity
9494
allowed_abs_error := getMaxAllowedAbsoluteTolerance(tt.want.HorizontalIntensity)
95-
compare = isClose(got.HorizontalIntensity, tt.want.HorizontalIntensity, allowed_relative_error, allowed_abs_error)
95+
is_close := isClose(got.HorizontalIntensity, tt.want.HorizontalIntensity, allowed_relative_error, allowed_abs_error)
96+
is_close_no_frac := noFracComparison(got.HorizontalIntensity, tt.want.HorizontalIntensity)
97+
compare = is_close || is_close_no_frac
98+
// For lat=-64.081, lon=135.866, alt=0, data=2021.5, horizontal intensity is 74.38, whereas reference value is 74.
99+
// Fortran rounds/truncates almost all values, therefore it's hard to use either relative or absolute tolerance.
100+
// noFracComparison compares a value with it's rounded/truncated value.
96101
if !compare {
97102
t.Errorf("IGRF() Horizontal intensity = %v, want %v", got.HorizontalIntensity, tt.want.HorizontalIntensity)
98103
}
@@ -185,6 +190,17 @@ func getMaxAllowedAbsoluteTolerance(value float64) float64 {
185190
return abs_tol
186191
}
187192

193+
func noFracComparison(val, ref float64) bool {
194+
// Since almost all values produces by Fortran are rounded/truncated, it's sometimes needed to compare
195+
// calculated value with rounded/truncated value. Rounding doesn't always work, as it's unclear whether Fortran values
196+
// are actually rounded or truncated.
197+
// Therefore values are truncated and compared to be within certain range.
198+
min := ref - 1.0
199+
max := ref + 1.0
200+
val_trunc := math.Floor((val))
201+
return min <= val && val_trunc <= max
202+
}
203+
188204
func isClose(a, b, rel_tol, abs_tol float64) bool {
189205
abs_diff := math.Abs(a - b)
190206
a_abs := math.Abs(a)
@@ -222,8 +238,8 @@ func produceTestsDataFromFile(file_descr *os.File) []testsData {
222238
scanner := bufio.NewScanner(file_descr)
223239
split_regex := regexp.MustCompile(`\s+`)
224240
var lat, lon, alt float64
225-
tests := make([]testsData, 125)
226-
for num, i := 0, 0; scanner.Scan(); num++ {
241+
var tests []testsData
242+
for num := 0; scanner.Scan(); num++ {
227243
line := scanner.Text()
228244
if num == 1 {
229245
// this is just a column names
@@ -244,8 +260,7 @@ func produceTestsDataFromFile(file_descr *os.File) []testsData {
244260
want: igrf_res,
245261
wantErr: false,
246262
}
247-
tests[i] = current_test
248-
i++
263+
tests = append(tests, current_test)
249264
}
250265
return tests
251266
}

testdata/set1

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Lat 59.900 geodetic Long -109.900 1.100 km
1+
Lat 59.900 geodetic Long -109.900 1.100 km
22
DATE D SV I SV H SV X SV Y SV Z SV F SV
33
1900.5 33.01 -2 82.33 0 8436 4 7074 7 4596 -3 62650 -20 63216 -19
44
1901.5 32.97 -2 82.33 0 8440 4 7081 7 4593 -3 62630 -20 63196 -19
@@ -115,13 +115,18 @@ Lat 59.900 geodetic Long -109.900 1.100 km
115115
2012.5 14.63 -17 80.21 -4 10020 43 9695 54 2531 -37 58088 -111 58946 -102
116116
2013.5 14.35 -17 80.15 -4 10063 43 9749 54 2494 -37 57977 -111 58844 -102
117117
2014.5 14.07 -17 80.09 -4 10106 43 9803 54 2457 -37 57867 -111 58743 -101
118-
2015.5 13.87 -7 80.03 -4 10152 48 9855 51 2434 -9 57760 -103 58645 -93
119-
2016.5 13.75 -7 79.97 -4 10199 48 9907 51 2425 -9 57657 -103 58553 -93
120-
2017.5 13.64 -7 79.90 -4 10247 48 9958 51 2416 -9 57555 -103 58460 -93
121-
2018.5 13.52 -7 79.84 -4 10295 48 10009 51 2406 -9 57452 -103 58367 -93
122-
2019.5 13.40 -7 79.78 -4 10342 48 10061 51 2397 -9 57350 -103 58275 -93
123-
2020.5 13.29 -6 79.71 -4 10391 49 10112 52 2389 -7 57250 -96 58186 -85
124-
2021.5 13.19 -6 79.65 -4 10439 49 10164 52 2382 -7 57155 -96 58100 -85
125-
2022.5 13.09 -6 79.58 -4 10488 49 10215 52 2376 -7 57059 -96 58015 -85
126-
2023.5 12.99 -6 79.52 -4 10537 49 10267 52 2369 -7 56963 -96 57930 -85
127-
2024.5 12.90 -6 79.46 -4 10585 49 10318 52 2362 -7 56868 -96 57844 -85
118+
2015.5 13.87 -7 80.03 -4 10152 47 9855 51 2434 -10 57760 -103 58645 -93
119+
2016.5 13.75 -7 79.97 -4 10199 47 9907 51 2425 -10 57657 -103 58553 -93
120+
2017.5 13.63 -7 79.91 -4 10247 48 9958 51 2415 -10 57555 -103 58460 -93
121+
2018.5 13.51 -7 79.84 -4 10294 48 10009 51 2406 -10 57452 -103 58367 -93
122+
2019.5 13.40 -7 79.78 -4 10342 48 10060 51 2396 -10 57349 -103 58274 -93
123+
2020.5 13.28 -7 79.71 -4 10391 50 10113 54 2387 -8 57251 -94 58186 -83
124+
2021.5 13.17 -7 79.65 -4 10441 50 10166 54 2380 -8 57157 -94 58103 -83
125+
2022.5 13.07 -6 79.58 -4 10491 50 10220 54 2372 -8 57063 -94 58020 -83
126+
2023.5 12.96 -6 79.52 -4 10542 50 10273 54 2364 -8 56970 -94 57937 -83
127+
2024.5 12.85 -6 79.45 -4 10592 50 10327 54 2356 -8 56876 -94 57854 -83
128+
2025.5 12.73 -9 79.38 -4 10643 52 10382 57 2345 -15 56778 -102 57767 -91
129+
2026.5 12.58 -9 79.31 -4 10696 52 10439 57 2330 -15 56676 -102 57676 -91
130+
2027.5 12.44 -9 79.24 -4 10748 52 10496 57 2314 -15 56574 -102 57586 -90
131+
2028.5 12.29 -9 79.17 -4 10800 52 10552 57 2299 -15 56472 -102 57495 -90
132+
2029.5 12.15 -8 79.10 -4 10852 52 10609 57 2284 -15 56370 -102 57405 -90

0 commit comments

Comments
 (0)