Solar wind example (SWACE + multiple models)#

This notebook shows how to use SWACE and read_solar_wind_from_multiple_models with synthetic solar wind data. It writes small fake data files to a local folder so you can run without downloads.

[1]:
from datetime import datetime, timezone
from pathlib import Path

import numpy as np
import pandas as pd

from swvo.io.solar_wind import (  # noqa: E402
    SWACE,
    SWOMNI,
    read_solar_wind_from_multiple_models,
)
from swvo.logger import setup_logging

setup_logging()


def find_repo_root(start: Path) -> Path:
    for candidate in [start, *start.parents]:
        if (candidate / "swvo").exists():
            return candidate
    raise RuntimeError("Could not locate repo root containing 'swvo' package.")


ROOT = find_repo_root(Path.cwd())
DATA_ROOT = ROOT / "docs" / "_notebook_data" / "solar_wind"
ACE_DIR = DATA_ROOT / "ACE_RT"
OMNI_DIR = DATA_ROOT / "OMNI_HIGH_RES"
ACE_DIR.mkdir(parents=True, exist_ok=True)
OMNI_DIR.mkdir(parents=True, exist_ok=True)

ROOT, DATA_ROOT
[1]:
(PosixPath('/PAGER/FLAG/code/external_data/SWVO'),
 PosixPath('/PAGER/FLAG/code/external_data/SWVO/docs/_notebook_data/solar_wind'))
[2]:
# Generate synthetic ACE (processed) and OMNI files
rng = np.random.default_rng(42)
start = datetime(2024, 11, 20, 0, 0, tzinfo=timezone.utc)
end = datetime(2024, 11, 20, 6, 0, tzinfo=timezone.utc)
idx = pd.date_range(start, end, freq="1min", tz="UTC")

ace_df = pd.DataFrame(
    {
        "t": idx,
        "bavg": 5 + rng.normal(0, 0.3, len(idx)),
        "bx_gsm": rng.normal(0, 1.0, len(idx)),
        "by_gsm": rng.normal(0, 1.0, len(idx)),
        "bz_gsm": rng.normal(0, 1.0, len(idx)),
        "proton_density": 6 + rng.normal(0, 0.5, len(idx)),
        "speed": 420 + rng.normal(0, 20.0, len(idx)),
        "temperature": 1.2e5 + rng.normal(0, 1.0e4, len(idx)),
    }
)
# Add a small gap to demonstrate model fallback
ace_df.iloc[30:50, ace_df.columns.get_indexer(["speed", "proton_density"])] = np.nan
Path(ACE_DIR / "2024" / "11").mkdir(parents=True, exist_ok=True)
ace_path = Path(ACE_DIR / "2024" / "11") / "ACE_SW_NOWCAST_20241120.csv"
ace_df.to_csv(ace_path, index=False)

omni_df = pd.DataFrame(
    {
        "timestamp": idx,
        "bavg": 5.2 + rng.normal(0, 0.2, len(idx)),
        "bx_gsm": rng.normal(0, 1.0, len(idx)),
        "by_gsm": rng.normal(0, 1.0, len(idx)),
        "bz_gsm": rng.normal(0, 1.0, len(idx)),
        "speed": 430 + rng.normal(0, 15.0, len(idx)),
        "proton_density": 6.5 + rng.normal(0, 0.4, len(idx)),
        "temperature": 1.1e5 + rng.normal(0, 8.0e3, len(idx)),
    }
)
omni_df["pdyn"] = 2e-6 * omni_df["proton_density"] * omni_df["speed"] ** 2
omni_df["sym-h"] = -10 + rng.normal(0, 5.0, len(idx))
Path(OMNI_DIR / "2024").mkdir(parents=True, exist_ok=True)
omni_path = Path(OMNI_DIR / "2024") / "OMNI_HIGH_RES_1min_202411.csv"
omni_df.to_csv(omni_path, index=False)

ace_path, omni_path
[2]:
(PosixPath('/PAGER/FLAG/code/external_data/SWVO/docs/_notebook_data/solar_wind/ACE_RT/2024/11/ACE_SW_NOWCAST_20241120.csv'),
 PosixPath('/PAGER/FLAG/code/external_data/SWVO/docs/_notebook_data/solar_wind/OMNI_HIGH_RES/2024/OMNI_HIGH_RES_1min_202411.csv'))

Read ACE data directly with SWACE#

[3]:
swace = SWACE(data_dir=ACE_DIR)
ace = swace.read(start, end, download=False)
ace.head()
[INFO    ] 2026-02-11 16:15:41 - swvo.io.solar_wind.ace - ACE data directory: /PAGER/FLAG/code/external_data/SWVO/docs/_notebook_data/solar_wind/ACE_RT
[3]:
bavg bx_gsm by_gsm bz_gsm file_name proton_density speed temperature
2024-11-20 00:00:00+00:00 5.091415 0.383394 -2.280738 0.061916 /PAGER/FLAG/code/external_data/SWVO/docs/_note... 5.927986 381.581681 148126.492482
2024-11-20 00:01:00+00:00 4.688005 0.999824 -1.496639 0.110396 /PAGER/FLAG/code/external_data/SWVO/docs/_note... 6.145020 429.540703 102907.738961
2024-11-20 00:02:00+00:00 5.225135 -1.058536 -0.922886 -0.408333 /PAGER/FLAG/code/external_data/SWVO/docs/_note... 7.162890 419.988738 112616.234191
2024-11-20 00:03:00+00:00 5.282169 -0.125009 1.461179 -1.398110 /PAGER/FLAG/code/external_data/SWVO/docs/_note... 6.758220 420.793871 113838.249158
2024-11-20 00:04:00+00:00 4.414689 1.481456 0.282587 -1.543625 /PAGER/FLAG/code/external_data/SWVO/docs/_note... 5.846717 439.214686 111491.803776

Read from multiple models (ACE first, OMNI fills gaps)#

[4]:
combined = read_solar_wind_from_multiple_models(
    start_time=start,
    end_time=end,
    model_order=[SWACE(ACE_DIR), SWOMNI(OMNI_DIR)],
    historical_data_cutoff_time=end,
    download=False,
)
combined.head()
[INFO    ] 2026-02-11 16:15:41 - swvo.io.solar_wind.ace - ACE data directory: /PAGER/FLAG/code/external_data/SWVO/docs/_notebook_data/solar_wind/ACE_RT
[INFO    ] 2026-02-11 16:15:41 - swvo.io.omni.omni_high_res - OMNI high resolution data directory: /PAGER/FLAG/code/external_data/SWVO/docs/_notebook_data/solar_wind/OMNI_HIGH_RES
[INFO    ] 2026-02-11 16:15:41 - swvo.io.solar_wind.read_solar_wind_from_multiple_models - Reading ace from 2024-11-20 00:00:00+00:00 to 2024-11-20 06:00:00+00:00
[INFO    ] 2026-02-11 16:15:41 - swvo.io.solar_wind.ace - Shiting start day by -1 day to account for propagation
[INFO    ] 2026-02-11 16:15:41 - swvo.io.utils - Percentage of NaNs in data frame: 176.45%
[INFO    ] 2026-02-11 16:15:41 - swvo.io.solar_wind.read_solar_wind_from_multiple_models - Reading omni from 2024-11-20 00:00:00+00:00 to 2024-11-20 06:00:00+00:00
[INFO    ] 2026-02-11 16:15:42 - swvo.io.utils - Percentage of NaNs in data frame: 0.00%
[4]:
bavg bx_gsm by_gsm bz_gsm file_name proton_density speed temperature model
2024-11-20 00:00:00+00:00 5.125716 -0.397194 0.185835 0.645113 /PAGER/FLAG/code/external_data/SWVO/docs/_note... 7.111424 433.291040 104055.036740 omni
2024-11-20 00:01:00+00:00 5.416470 -0.332443 1.149377 -0.765495 /PAGER/FLAG/code/external_data/SWVO/docs/_note... 6.908618 433.518934 109031.772674 omni
2024-11-20 00:02:00+00:00 5.091603 -0.817558 -0.044733 -0.579440 /PAGER/FLAG/code/external_data/SWVO/docs/_note... 6.730933 455.511891 112888.008829 omni
2024-11-20 00:03:00+00:00 5.429651 0.421617 -1.405164 -0.463445 /PAGER/FLAG/code/external_data/SWVO/docs/_note... 6.158006 419.974924 115729.565918 omni
2024-11-20 00:04:00+00:00 5.308439 -0.094015 0.966130 -1.852728 /PAGER/FLAG/code/external_data/SWVO/docs/_note... 6.560636 434.441509 109248.865388 omni
[5]:
combined["model"].value_counts(dropna=False)
[5]:
model
ace     270
omni     91
Name: count, dtype: int64

Notes#

  • read_solar_wind_from_multiple_models fills missing values by model priority.

  • In this example, ACE is preferred and OMNI fills the synthetic gaps.

  • For real data, pass download=True and set the environment variables in the class docs (e.g. RT_SW_ACE_STREAM_DIR).