Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
87d5ae0
v2.0.0
Demosfen Oct 25, 2025
eed3761
#42-Implement-CommonContext (#44)
Demosfen Oct 30, 2025
ffd658c
#43-Refactor (#64)
Demosfen Nov 6, 2025
c2a5e7c
Merge branch 'main' into 2.0.0-dev
Demosfen Nov 6, 2025
5754615
#43-DI (#66)
Demosfen Nov 6, 2025
393e689
Merge branch 'main' into 2.0.0-dev
Demosfen Nov 12, 2025
9d4018a
+
Demosfen Nov 12, 2025
b5e8918
+
Demosfen Nov 12, 2025
be4fdaa
+
Demosfen Nov 17, 2025
c64dec3
+
Demosfen Nov 17, 2025
6b30c1b
Merge branch 'main' into 2.0.0-dev
Demosfen Nov 24, 2025
1d2468a
+
Demosfen Nov 24, 2025
0555b1f
#46 - Code optimizations (#70)
Demosfen Dec 28, 2025
583d13f
ILogger & stackalloc (#71)
Demosfen Jan 30, 2026
1631188
+ Linux benchmarks
Demosfen Jan 30, 2026
18a45ca
+ fix Vector Exception
Demosfen Jan 30, 2026
2aa5c04
+ fix Vectorization ver2
Demosfen Jan 30, 2026
e42e67d
+ MacOS bench
Demosfen Feb 1, 2026
1b2d790
+
Demosfen Feb 2, 2026
ceba05f
+
Demosfen Feb 3, 2026
38b0512
+ bench
Demosfen Feb 3, 2026
9b258b6
+
Demosfen Feb 3, 2026
735c9f5
+
Demosfen Feb 3, 2026
c23f2ea
+
Demosfen Feb 3, 2026
0efc1e6
+
Demosfen Feb 4, 2026
4e2cd8b
+ clear bench
Demosfen Feb 4, 2026
5e0b79b
+ clean benchmarks
Demosfen Feb 4, 2026
4fe7598
+
Demosfen Feb 4, 2026
f4b3331
+ CH_LOG & PackageReleaseNotes
Demosfen Feb 4, 2026
444913b
+ tests refactor
Demosfen Feb 4, 2026
3c864c1
+
Demosfen Feb 4, 2026
d72f8db
+
Demosfen Feb 4, 2026
6b17965
+
Demosfen Feb 4, 2026
54d4daa
+
Demosfen Feb 4, 2026
53a7f4a
pre-release
Demosfen Feb 4, 2026
8a1df3d
+
Demosfen Feb 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Changelog

All notable changes to the AuroraScienceHub.Geopack project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.0.0] - 2026-02-04

### Added

- Immutable `ComputationContext` record to hold pre-calculated coefficients for thread-safe operations
- Strongly-typed generic vector quantities: `CartesianVector<T>` and `SphericalVector<T>`
- Dependency injection support via `ServiceCollectionExtensions`
- `AddGeopack()` extension method for registering `IGeopack` implementation
- `AddExternalFieldModels()` extension method for registering `IT89` implementation
- `GeopackConstants` class containing well-documented physics and algorithm constants
- Null-checking extension methods in `ObjectExtensions` for parameter validation
- Explicit exception throwing for invalid inputs instead of returning NaN values
- SIMD vectorization using `Vector<double>` for IGRF coefficient interpolation and extrapolation
- Capacity hints for list initialization to reduce memory allocations

### Changed

- **BREAKING**: All coordinate transformation and field calculation methods now require `ComputationContext` parameter
- **BREAKING**: Method signatures refactored to accept strongly-typed location and vector objects instead of individual coordinate parameters
- **BREAKING**: Data model records converted from reference types to `readonly record struct` for improved performance
- **BREAKING**: Coordinate and vector transformations converted from standalone methods to instance methods on model types
- **BREAKING**: Exception handling updated to throw explicit exceptions if incorrect coordinate systems are provided or inputs are invalid
- `IGeopack` interface updated to use new strongly-typed contracts
- `Geopack` main class now requires `ILogger` via dependency injection
- Replaced `Math.Pow(x, 2)` with direct multiplication `x * x` throughout codebase
- Adopted `Math.SinCos()` for simultaneous sine and cosine calculations
- Optimized Newton's method iteration extracted to separate method
- Cached repeated trigonometric calculations to avoid recomputation
- Eliminated zero multiplications in coefficient processing
- Optimized dot product calculations in trace methods

### Removed

- **BREAKING**: Mutable shared state classes `Common1` and `Common2` removed
- **BREAKING**: Methods that relied on shared mutable state

### Fixed

- Thread-safety issues caused by mutable shared state
- Performance bottlenecks in mathematical operations

### Performance

- Significant performance improvements through SIMD vectorization
- Reduced memory allocations via `readonly record struct` conversion
- Optimized trigonometric calculations with `Math.SinCos()`
- Improved computational efficiency by eliminating redundant calculations
- Enhanced list operations with pre-allocated capacity

### Migration from v1.x to v2.0.0

#### Required Changes

1. **ComputationContext Parameter**

Before:
```csharp
geopack.GeoToGsw(xGeo, yGeo, zGeo, out xGsw, out yGsw, out zGsw);
```

After:
```csharp
var context = geopack.Recalc(dateTime, CartesianVector<Velocity>.New(vxGse, vyGse, vzGse, CoordinateSystem.GSE));
var result = geopack.GeoToGsw(context, CartesianLocation.New(xGeo, yGeo, zGeo, CoordinateSystem.GEO));
```

2. **Strongly-Typed Objects**

Before:
```csharp
geopack.IgrfGeo(xGeo, yGeo, zGeo, out hxGeo, out hyGeo, out hzGeo);
```

After:
```csharp
var location = CartesianLocation.New(xGeo, yGeo, zGeo, CoordinateSystem.GEO);
var fieldVector = geopack.IgrfGeo(context, location);
```

3. **Dependency Injection Setup**

```csharp
services.AddGeopack();
services.AddExternalFieldModels();
```

4. **Exception Handling**

After: Methods throw exceptions for invalid inputs and incorrect coordinate systems. This increase code robustness but requires updating error handling in calling code.

#### Additional Considerations

- Review code that relied on mutable shared state (Common1/Common2)
- Update unit tests to use new API patterns
- Verify exception handling covers new explicit exceptions
- Consider leveraging dependency injection for better testability
- Check that readonly structs are passed by reference where appropriate for performance

2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</PropertyGroup>

<PropertyGroup>
<PackageBaseVersion>1.0.3</PackageBaseVersion>
<PackageBaseVersion>2.0.0</PackageBaseVersion>
<MinVerTagPrefix></MinVerTagPrefix>
</PropertyGroup>

Expand Down
4 changes: 3 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<EnablePackageVersionOverride>true</EnablePackageVersionOverride>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.15.4" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
Expand All @@ -14,6 +14,8 @@
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.14.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageVersion Include="MinVer" Version="6.0.0" />
<PackageVersion Include="Shouldly" Version="4.3.0" />
Expand Down
3 changes: 2 additions & 1 deletion Geopack.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<Folder Name="/docs/">
<File Path="docs/NuGetPackages.md" />
<File Path="docs\ReleaseNotes_GeopackV1.md" />
<File Path="docs\ReleaseNotes_GeopackV2.md" />
</Folder>
<Folder Name="/GitHub/" />
<Folder Name="/GitHub/workflows/">
Expand All @@ -31,4 +32,4 @@
<Folder Name="/tests/">
<Project Path="UnitTests/UnitTests.csproj" />
</Folder>
</Solution>
</Solution>
5 changes: 3 additions & 2 deletions NuGet.Config
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*"/>
<package pattern="AuroraScienceHub.*" />
<package pattern="*" />
</packageSource>
<packageSource key="AuroraScienceHub">
<package pattern="AuroraScienceHub.*"/>
<package pattern="AuroraScienceHub.*" />
</packageSource>
</packageSourceMapping>
<disabledPackageSources>
Expand Down
134 changes: 113 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,26 @@
High-performance C# implementation of the Geopack-2008 geomagnetic field model with double-precision accuracy.
<br><br>

[![](https://img.shields.io/badge/.NET-9.0-512BD4)](https://dotnet.microsoft.com/)
[![](https://img.shields.io/badge/C%23-13.0-239120)](https://learn.microsoft.com/en-us/dotnet/csharp/)
[![NuGet Version](https://img.shields.io/nuget/v/AuroraScienceHub.Geopack?logo=nuget&label=NuGet)](https://www.nuget.org/packages/AuroraScienceHub.Geopack/)
[![NuGet Downloads](https://img.shields.io/nuget/dt/AuroraScienceHub.Geopack?logo=nuget&label=Downloads)](https://www.nuget.org/packages/AuroraScienceHub.Geopack/)
[![](https://img.shields.io/badge/.NET-8.0%20%7C%2010.0-512BD4?logo=dotnet)](https://dotnet.microsoft.com/)
[![](https://img.shields.io/badge/C%23-13.0-239120?logo=csharp)](https://learn.microsoft.com/en-us/dotnet/csharp/)
<br>
[![Build & Test](https://github.com/Aurora-Science-Hub/Geopack/actions/workflows/dotnet.yml/badge.svg)](https://github.com/Aurora-Science-Hub/Geopack/actions/workflows/dotnet.yml)
[![License: GPL v3+](https://img.shields.io/badge/License-GPLv3+-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![DOI](https://zenodo.org/badge/782457774.svg)](https://doi.org/10.5281/zenodo.17437549)
[![Build & test](https://github.com/Aurora-Science-Hub/Geopack/actions/workflows/dotnet.yml/badge.svg)](https://github.com/Aurora-Science-Hub/Geopack/actions/workflows/dotnet.yml)
[![GitHub Stars](https://img.shields.io/github/stars/Aurora-Science-Hub/Geopack?style=social)](https://github.com/Aurora-Science-Hub/Geopack)

<a href="#features">Features</a> •
<a href="#installation">Installation</a> •
<a href="#quick-start">Quick Start</a> •
<a href="#validation">Validation</a> •
<a href="#benchmarks">Benchmarks</a> •
<a href="#tech-stack">Tech stack</a> •
<a href="#native-aot-compilation">Code Style</a> •
<a href="#changelog">Changelog</a> •
<a href="#tech-stack">Tech Stack</a> •
<a href="#native-aot-compilation">Native AOT</a> •
<a href="#licensing">Licensing</a> •
<a href="#how-to-cite">How to cite</a> •
<a href="#how-to-cite">How to Cite</a> •
<a href="#references">References</a>

</div>
Expand All @@ -34,6 +42,66 @@
This library provides numerical accuracy matching the original Fortran code by N. A. Tsyganenko to within 12 decimal digits (`8E-12D`).
For external magnetic field models, accuracy is raised to 13 digits (`1E-13D`).

## Features

- **High Precision**: Numerical accuracy matching original Fortran code to 12-13 decimal digits
- **Thread-Safe**: Immutable ComputationContext pattern eliminates shared mutable state
- **Type-Safe**: Strongly-typed generic vector quantities for Cartesian and spherical coordinates
- **Performance Optimized**: SIMD vectorization, Math.SinCos, and optimized mathematical operations
- **Modern .NET**: Native AOT compilation support, nullable reference types, C# 13 features
- **Dependency Injection**: Built-in DI support with ServiceCollectionExtensions
- **Comprehensive Testing**: 100+ unit tests validated against original Fortran implementation
- **Well Documented**: Clear API documentation and extensive benchmarks

## Installation

Install the package via NuGet Package Manager:

```shell
dotnet add package AuroraScienceHub.Geopack
```

Or via Package Manager Console:

```powershell
Install-Package AuroraScienceHub.Geopack
```

For external field models (T89, T96, etc.):

```shell
dotnet add package AuroraScienceHub.Geopack.ExternalFieldModels
```

## Quick Start

### Basic Usage

```csharp
using AuroraScienceHub.Geopack;
using AuroraScienceHub.Geopack.Contracts.Cartesian;
using AuroraScienceHub.Geopack.Contracts.Coordinates;
using AuroraScienceHub.Geopack.Contracts.PhysicalQuantities;

// Create Geopack instance
var geopack = new Geopack();

// Define date/time and solar wind velocity
var dateTime = new DateTime(1997, 12, 21, 21, 0, 0, DateTimeKind.Utc);
var swVelocity = CartesianVector<Velocity>.New(-304.0, 13.0, 4.0, CoordinateSystem.GSE);

// Calculate computation context
var context = geopack.Recalc(dateTime, swVelocity);

// Transform coordinates GEO -> GSW
var geoLocation = CartesianLocation.New(1.0, 2.0, 3.0, CoordinateSystem.GEO);
var gswLocation = geopack.GeoToGsw(context, geoLocation);

// Calculate IGRF magnetic field
var fieldVector = geopack.IgrfGeo(context, geoLocation);
Console.WriteLine($"Magnetic field: Bx={fieldVector.X}, By={fieldVector.Y}, Bz={fieldVector.Z}");
```

## Validation
The implementation is rigorously validated against the original Fortran code using our comprehensive testing framework with 100+ unit tests.
See [Unit Testing Framework](UnitTests/README.md) for details on test data generation and verification procedures.
Expand All @@ -44,9 +112,24 @@ Comprehensive performance benchmarks are available to measure the library's effi
For detailed benchmark results, methodology, and running instructions,
see the [benchmarks documentation](benchmarks/AuroraScienceHub.Geopack.Benchmarks/README.md).

## Tech stack
## Changelog

### Version 2.0.0 (Latest)

This is a major release with significant architectural improvements and breaking changes. Key updates include:

- **Thread-Safety**: Replaced mutable shared state with immutable ComputationContext pattern
- **Strongly-Typed API**: Generic vector quantities (CartesianVector<T>, SphericalVector<T>)
- **Performance**: SIMD vectorization, Math.SinCos, optimized mathematical operations
- **Data Models**: Converted to readonly record structs for better performance
- **Dependency Injection**: Full DI support with ServiceCollectionExtensions

⚠️ **Breaking Changes**: Method signatures have changed. See [CHANGELOG.md](CHANGELOG.md) for detailed migration guide.

## Tech Stack
- Supported .NET versions:
- [.NET 9](https://dotnet.microsoft.com/en-us/download/dotnet/9.0)
- [.NET 8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
- [.NET 10](https://dotnet.microsoft.com/en-us/download/dotnet/10.0)
- [Native AOT compilation](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/)
- [Nullable reference types](https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references)
- [Central package management](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management)
Expand All @@ -56,13 +139,13 @@ see the [benchmarks documentation](benchmarks/AuroraScienceHub.Geopack.Benchmark
To build the project with native AOT compilation, execute the following command (depending on the target platform):

```shell
dotnet publish --framework net9.0 -c Release -r linux-x64
dotnet publish --framework net10.0 -c Release -r linux-x64
```
```shell
dotnet publish --framework net9.0 -c Release -r win-x64
dotnet publish --framework net10.0 -c Release -r win-x64
```
```shell
dotnet publish --framework net9.0 -c Release -r osx-x64
dotnet publish --framework net10.0 -c Release -r osx-x64
```

## Licensing
Expand All @@ -73,19 +156,28 @@ This C# implementation is a derivative work of the original FORTRAN code by Niko
If you use this software in your research, please cite it using the following metadata:

**APA Style:**
Nikolaev, A. V., Ermilov, A. O., & Tsyganenko, N. A. (2025). *Geopack-2008 C# .NET implementation* (Version v1.0.0) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.17437549
Nikolaev, A., Ermilov, A., & Tsyganenko, N. (2026). Geopack-2008 C# .NET implementation (v1.0.3). Zenodo. https://doi.org/10.5281/zenodo.17441603

**BibTeX:**
```bibtex
@software{Geopack_2008_CSharp_2025,
author = {Nikolaev, Alexander V. and Ermilov, Aleksei O. and Tsyganenko, Nikolai A.},
doi = {10.5281/zenodo.17437549},
license = {GPL-3.0-or-later},
month = oct,
title = {{Geopack-2008 C\# .NET implementation}},
url = {https://github.com/Aurora-Science-Hub/Geopack},
version = {v1.0.0},
year = {2025}
@software{nikolaev_2026_17441603,
author = {Nikolaev, Alexander and
Ermilov, Aleksei and
Tsyganenko, Nikolai},
title = {Geopack-2008 C\# .NET implementation},
month = jan,
year = 2026,
publisher = {Zenodo},
version = {v1.0.3},
doi = {10.5281/zenodo.17441603},
url = {https://doi.org/10.5281/zenodo.17441603},
swhid = {swh:1:dir:6803d108518ce91041b35fc1ad3ac77fb24ae680
;origin=https://doi.org/10.5281/zenodo.17437549;vi
sit=swh:1:snp:7e657a3370ceade1723538a54de709ceae47
cf88;anchor=swh:1:rel:60165de1aba64c409a029ed9c082
6d500f400274;path=Aurora-Science-Hub-
Geopack-96b844e
},
}
```

Expand Down
14 changes: 14 additions & 0 deletions UnitTests/Extensions/TestExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Shouldly;

namespace AuroraScienceHub.Geopack.UnitTests.Extensions;

/// <summary>
/// Test extensions
/// </summary>
public static class TestExtensions
{
private const double MinimalTestsPrecision = 0.000000000008d;

internal static void ShouldApproximatelyBe(this double actual, double expected)
=> actual.ShouldBe(expected, MinimalTestsPrecision);
}
15 changes: 10 additions & 5 deletions UnitTests/ExternalFieldModels/ExternalFieldModelsTests.T89.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using AuroraScienceHub.Geopack.Contracts.Models;
using AuroraScienceHub.Geopack.Contracts.Cartesian;
using AuroraScienceHub.Geopack.Contracts.Coordinates;
using AuroraScienceHub.Geopack.Contracts.PhysicalQuantities;
using Shouldly;

namespace AuroraScienceHub.Geopack.UnitTests.ExternalFieldModels;
Expand All @@ -20,12 +22,15 @@ public void T89_ShouldReturnCorrectValues(
double x, double y, double z,
double expectedBx, double expectedBy, double expectedBz)
{
// Arrange
CartesianLocation location = CartesianLocation.New(x, y, z, CoordinateSystem.GSW);

// Act
CartesianFieldVector resultField = _t89.Calculate(iopt, new double[10], ps, x, y, z);
CartesianVector<MagneticField> resultField = _t89.Calculate(iopt, new double[10], ps, location);

// Assert
resultField.Bx.ShouldBe(expectedBx, MinimalTestsPrecision);
resultField.By.ShouldBe(expectedBy, MinimalTestsPrecision);
resultField.Bz.ShouldBe(expectedBz, MinimalTestsPrecision);
resultField.X.ShouldBe(expectedBx, MinimalTestsPrecision);
resultField.Y.ShouldBe(expectedBy, MinimalTestsPrecision);
resultField.Z.ShouldBe(expectedBz, MinimalTestsPrecision);
}
}
Loading