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_modelsfills missing values by model priority.In this example, ACE is preferred and OMNI fills the synthetic gaps.
For real data, pass
download=Trueand set the environment variables in the class docs (e.g.RT_SW_ACE_STREAM_DIR).