diff --git a/docs/swapi/alpha-sw.md b/docs/swapi/alpha-sw.md index eae0733f..ecfee77f 100644 --- a/docs/swapi/alpha-sw.md +++ b/docs/swapi/alpha-sw.md @@ -76,21 +76,33 @@ Finally, the `alpha-sw` CDF variables are derived from the fitted parameter vect | CDF variable | Symbol | Definition | |---------------------------------------|---------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------| -| `epoch` | $`t_{\text{center}}`$ | center time of the 5-sweep chunk | -| `epoch_delta` | | $`30\thinspace \text{s}`$ (half-width of the 5-sweep chunk) | -| `alpha_sw_density` | $`n^{\alpha}`$ | $`\exp(x^{\alpha}_{0})`$ | -| `alpha_sw_density_uncert` | $`\sigma_{n^{\alpha}}`$ | $`n^{\alpha}\thinspace \sqrt{\Sigma_{x^{\alpha}}[0, 0]}`$ | -| `alpha_sw_temperature` | $`T^{\alpha}`$ | $`\exp(x^{\alpha}_{1})`$ | -| `alpha_sw_temperature_uncert` | $`\sigma_{T^{\alpha}}`$ | $`T^{\alpha}\thinspace \sqrt{\Sigma_{x^{\alpha}}[1, 1]}`$ | -| `alpha_sw_velocity_rtn` | $`\mathbf{v}^{\alpha,\text{SC}}`$ | $`\mathbf{v}_{b}^{\text{SC}} + x^{\alpha}_{2}\thinspace \hat{\mathbf{B}}`$ [1](#fn-vp) | -| `alpha_sw_velocity_rtn_covariance` | $`\Sigma_{\mathbf{v}^{\alpha}}`$ | $`\Sigma_{\mathbf{v}^{p}} + \Sigma_{x^{\alpha}}[2,2 ]\thinspace \hat{\mathbf{B}}\hat{\mathbf{B}}^{\top}`$ | -| `alpha_sw_velocity_rtn_uncert` | $`\sigma_{\mathbf{v}^{\alpha}}`$ | $`\sqrt{\operatorname{diag}(\Sigma_{\mathbf{v}^{\alpha}})}`$ (same for `alpha_sw_velocity_rtn_sun`) | -| `alpha_sw_speed` | $`\lvert\mathbf{v}^{\alpha,\text{SC}}\rvert`$ | $`\lvert\mathbf{v}^{\alpha,\text{SC}}\rvert`$ | -| `alpha_sw_speed_uncert` | $`\sigma_{\lvert\mathbf{v}^{\alpha,\text{SC}}\rvert}`$ | propagated through `uncertainties` from $`\mathbf{v}^{\alpha,\text{SC}}`$ | -| `alpha_sw_velocity_rtn_sun` | $`\mathbf{v}^{\alpha,\text{sun}}`$ | $`\mathbf{v}^{\alpha,\text{SC}} + \mathbf{v}_{\text{sc}}^{\text{sun}}`$ | -| `alpha_sw_speed_sun` | $`\lvert\mathbf{v}^{\alpha,\text{sun}}\rvert`$ | $`\lvert\mathbf{v}^{\alpha,\text{sun}}\rvert`$ | +| `epoch` | $`t_{\text{center}}`$ | center time of the 5-sweep chunk | +| `epoch_delta` | | $`30\thinspace \text{s}`$ (half-width of the 5-sweep chunk) | +| `alpha_sw_density` | $`n^{\alpha}`$ | $`\exp(x^{\alpha}_{0})`$ | +| `alpha_sw_density_uncert` | $`\sigma_{n^{\alpha}}`$ | $`n^{\alpha}\thinspace \sqrt{\Sigma_{x^{\alpha}}[0, 0]}`$ | +| `alpha_sw_temperature` | $`T^{\alpha}`$ | $`\exp(x^{\alpha}_{1})`$ | +| `alpha_sw_temperature_uncert` | $`\sigma_{T^{\alpha}}`$ | $`T^{\alpha}\thinspace \sqrt{\Sigma_{x^{\alpha}}[1, 1]}`$ | +| `alpha_sw_velocity_rtn` | $`\mathbf{v}^{\alpha,\text{SC}}`$ | $`\mathbf{v}_{b}^{\text{SC}} + x^{\alpha}_{2}\thinspace \hat{\mathbf{B}}`$ [1](#fn-vp) | +| `alpha_sw_velocity_rtn_covariance` | $`\Sigma_{\mathbf{v}^{\alpha}}`$ | $`\Sigma_{\mathbf{v}^{p}} + \Sigma_{x^{\alpha}}[2,2 ]\thinspace \hat{\mathbf{B}}\hat{\mathbf{B}}^{\top}`$ | +| `alpha_sw_velocity_rtn_uncert` | $`\sigma_{\mathbf{v}^{\alpha}}`$ | $`\sqrt{\operatorname{diag}(\Sigma_{\mathbf{v}^{\alpha}})}`$ (same for `alpha_sw_velocity_rtn_sun`) | +| `alpha_sw_speed` | $`\lvert\mathbf{v}^{\alpha,\text{SC}}\rvert`$ | $`\lvert\mathbf{v}^{\alpha,\text{SC}}\rvert`$ | +| `alpha_sw_speed_uncert` | $`\sigma_{\lvert\mathbf{v}^{\alpha,\text{SC}}\rvert}`$ | propagated through `uncertainties` from $`\mathbf{v}^{\alpha,\text{SC}}`$ | +| `alpha_sw_velocity_rtn_sun` | $`\mathbf{v}^{\alpha,\text{sun}}`$ | $`\mathbf{v}^{\alpha,\text{SC}} + \mathbf{v}_{\text{sc}}^{\text{sun}}`$ | +| `alpha_sw_velocity_gse` | $`\mathbf{v}^{\alpha,\text{SC}}_{\text{GSE}}`$ | RTN spacecraft-frame velocity rotated to GSE coordinates | +| `alpha_sw_velocity_gse_sun` | $`\mathbf{v}^{\alpha,\text{sun}}_{\text{GSE}}`$ | RTN Sun-frame velocity rotated to GSE coordinates | +| `alpha_sw_velocity_gse_covariance` | $`\Sigma_{\mathbf{v}^{\alpha},\text{GSE}}`$ | $`R_{\text{GSE}}\Sigma_{\mathbf{v}^{\alpha}}R_{\text{GSE}}^{\top}`$ | +| `alpha_sw_velocity_gse_uncert` | $`\sigma_{\mathbf{v}^{\alpha},\text{GSE}}`$ | $`\sqrt{\operatorname{diag}(\Sigma_{\mathbf{v}^{\alpha},\text{GSE}})}`$ | +| `alpha_sw_velocity_gsm` | $`\mathbf{v}^{\alpha,\text{SC}}_{\text{GSM}}`$ | RTN spacecraft-frame velocity rotated to GSM coordinates | +| `alpha_sw_velocity_gsm_sun` | $`\mathbf{v}^{\alpha,\text{sun}}_{\text{GSM}}`$ | RTN Sun-frame velocity rotated to GSM coordinates | +| `alpha_sw_velocity_gsm_covariance` | $`\Sigma_{\mathbf{v}^{\alpha},\text{GSM}}`$ | $`R_{\text{GSM}}\Sigma_{\mathbf{v}^{\alpha}}R_{\text{GSM}}^{\top}`$ | +| `alpha_sw_velocity_gsm_uncert` | $`\sigma_{\mathbf{v}^{\alpha},\text{GSM}}`$ | $`\sqrt{\operatorname{diag}(\Sigma_{\mathbf{v}^{\alpha},\text{GSM}})}`$ | +| `alpha_sw_velocity_hae` | $`\mathbf{v}^{\alpha,\text{SC}}_{\text{HAE-J2000}}`$ | RTN spacecraft-frame velocity rotated to HAE-J2000 coordinates | +| `alpha_sw_velocity_hae_sun` | $`\mathbf{v}^{\alpha,\text{sun}}_{\text{HAE-J2000}}`$ | RTN Sun-frame velocity rotated to HAE-J2000 coordinates | +| `alpha_sw_velocity_hae_covariance` | $`\Sigma_{\mathbf{v}^{\alpha},\text{HAE-J2000}}`$ | $`R_{\text{HAE-J2000}}\Sigma_{\mathbf{v}^{\alpha}}R_{\text{HAE-J2000}}^{\top}`$ | +| `alpha_sw_velocity_hae_uncert` | $`\sigma_{\mathbf{v}^{\alpha},\text{HAE-J2000}}`$ | $`\sqrt{\operatorname{diag}(\Sigma_{\mathbf{v}^{\alpha},\text{HAE-J2000}})}`$ | +| `alpha_sw_speed_sun` | $`\lvert\mathbf{v}^{\alpha,\text{sun}}\rvert`$ | $`\lvert\mathbf{v}^{\alpha,\text{sun}}\rvert`$ | | `alpha_sw_speed_sun_uncert` | $`\sigma_{\lvert\mathbf{v}^{\alpha,\text{sun}}\rvert}`$ | propagated through `uncertainties` from $`\mathbf{v}^{\alpha,\text{sun}}`$ | -| `swp_flags` | | quality flag bitmask (see `SwapiL3Flags`) | +| `swp_flags` | | quality flag bitmask (see `SwapiL3Flags`) | diff --git a/docs/swapi/proton-sw.md b/docs/swapi/proton-sw.md index 6b486c59..d4350c3d 100644 --- a/docs/swapi/proton-sw.md +++ b/docs/swapi/proton-sw.md @@ -198,6 +198,18 @@ The `proton-sw` CDF variables are derived from $`\mathbf{x}`$ and $`\Sigma_\math | `proton_sw_speed` | $`\lvert\mathbf{v}_{b}^{\text{SC}}\rvert`$ | $`\lvert\mathbf{v}_{b}^{\text{SC}}\rvert`$ | | `proton_sw_speed_uncert` | $`\sigma_{\lvert\mathbf{v}_{b}^{\text{SC}}\rvert}`$ | propagated through `uncertainties` from $`\mathbf{v}_{b}^{\text{SC}}`$ | | `proton_sw_velocity_rtn_sun` | $`\mathbf{v}_{b}^{\text{sun}}`$ | $`\mathbf{v}_{b}^{\text{SC}} + \mathbf{v}_{\text{sc}}^{\text{sun}}`$ [2](#fn-vsc) | +| `proton_sw_velocity_gse` | $`\mathbf{v}_{b,\text{GSE}}^{\text{SC}}`$ | RTN spacecraft-frame velocity rotated to GSE coordinates | +| `proton_sw_velocity_gse_sun` | $`\mathbf{v}_{b,\text{GSE}}^{\text{sun}}`$ | RTN Sun-frame velocity rotated to GSE coordinates | +| `proton_sw_velocity_gse_covariance` | $`\Sigma_{\mathbf{v},\text{GSE}}`$ | $`R_{\text{GSE}}\Sigma_{\mathbf{v}}R_{\text{GSE}}^{\top}`$ | +| `proton_sw_velocity_gse_uncert` | $`\sigma_{\mathbf{v},\text{GSE}}`$ | $`\sqrt{\operatorname{diag}(\Sigma_{\mathbf{v},\text{GSE}})}`$ | +| `proton_sw_velocity_gsm` | $`\mathbf{v}_{b,\text{GSM}}^{\text{SC}}`$ | RTN spacecraft-frame velocity rotated to GSM coordinates | +| `proton_sw_velocity_gsm_sun` | $`\mathbf{v}_{b,\text{GSM}}^{\text{sun}}`$ | RTN Sun-frame velocity rotated to GSM coordinates | +| `proton_sw_velocity_gsm_covariance` | $`\Sigma_{\mathbf{v},\text{GSM}}`$ | $`R_{\text{GSM}}\Sigma_{\mathbf{v}}R_{\text{GSM}}^{\top}`$ | +| `proton_sw_velocity_gsm_uncert` | $`\sigma_{\mathbf{v},\text{GSM}}`$ | $`\sqrt{\operatorname{diag}(\Sigma_{\mathbf{v},\text{GSM}})}`$ | +| `proton_sw_velocity_hae` | $`\mathbf{v}_{b,\text{HAE-J2000}}^{\text{SC}}`$ | RTN spacecraft-frame velocity rotated to HAE-J2000 coordinates | +| `proton_sw_velocity_hae_sun` | $`\mathbf{v}_{b,\text{HAE-J2000}}^{\text{sun}}`$ | RTN Sun-frame velocity rotated to HAE-J2000 coordinates | +| `proton_sw_velocity_hae_covariance` | $`\Sigma_{\mathbf{v},\text{HAE-J2000}}`$ | $`R_{\text{HAE-J2000}}\Sigma_{\mathbf{v}}R_{\text{HAE-J2000}}^{\top}`$ | +| `proton_sw_velocity_hae_uncert` | $`\sigma_{\mathbf{v},\text{HAE-J2000}}`$ | $`\sqrt{\operatorname{diag}(\Sigma_{\mathbf{v},\text{HAE-J2000}})}`$ | | `proton_sw_speed_sun` | $`v_{\text{sun}}`$ | $`\lvert\mathbf{v}_{b}^{\text{sun}}\rvert`$ | | `proton_sw_speed_sun_uncert` | $`\sigma_{v_{\text{sun}}}`$ | propagated through `uncertainties` from $`\mathbf{v}_{b}^{\text{sun}}`$ | | `swp_flags` | | quality flag bitmask (see `SwapiL3Flags`) | @@ -207,4 +219,3 @@ The `proton-sw` CDF variables are derived from $`\mathbf{x}`$ and $`\Sigma_\math [1]: The square root of the diagonal of $`\Sigma_{\mathbf{v}}`$ yields the standard error of the $`v_{R}, v_{T}, v_{N}`$ components. The covariance is the same for `proton_sw_velocity_rtn_sun`. [2]: $`\mathbf{v}_{\text{sc}}^{\text{sun}}`$ is the spacecraft velocity in the Sun's inertial frame. - diff --git a/imap_l3_processing/cdf/config/imap_swapi_l3a_variable_attrs.yaml b/imap_l3_processing/cdf/config/imap_swapi_l3a_variable_attrs.yaml index 8a6cb768..1de180fa 100644 --- a/imap_l3_processing/cdf/config/imap_swapi_l3a_variable_attrs.yaml +++ b/imap_l3_processing/cdf/config/imap_swapi_l3a_variable_attrs.yaml @@ -386,6 +386,222 @@ proton_sw_velocity_rtn_uncert: VALIDMAX: 10000 FILLVAL: -1.000000e+31 SCALETYP: linear +proton_sw_velocity_gse_sun: + NAME: proton_sw_velocity_gse_sun + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity (GSE, Sun Frame) + CATDESC: Solar wind proton bulk velocity vector in GSE coordinates (Sun frame) + DEPEND_0: epoch + LABL_PTR_1: proton_sw_velocity_gse_sun_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Vp GSE Sun + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: proton_sw_velocity_gse_uncert + DELTA_MINUS_VAR: proton_sw_velocity_gse_uncert +proton_sw_velocity_gse: + NAME: proton_sw_velocity_gse + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity (GSE) + CATDESC: Solar wind proton bulk velocity vector in GSE coordinates (spacecraft frame) + DEPEND_0: epoch + LABL_PTR_1: proton_sw_velocity_gse_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Vp GSE SC + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: proton_sw_velocity_gse_uncert + DELTA_MINUS_VAR: proton_sw_velocity_gse_uncert +proton_sw_velocity_gse_covariance: + NAME: proton_sw_velocity_gse_covariance + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity Covariance (GSE) + CATDESC: Covariance matrix in GSE coordinates, with standard errors given by the square root of the diagonal + DEPEND_0: epoch + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: cov Vp + UNITS: km^2/s^2 + VALIDMIN: -1.0e+8 + VALIDMAX: 1.0e+8 + FILLVAL: -1.000000e+31 + LABL_PTR_1: velocity_gse_label + LABL_PTR_2: velocity_gse_label +proton_sw_velocity_gse_uncert: + NAME: proton_sw_velocity_gse_uncert + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity Uncertainty (GSE) + CATDESC: 1-sigma standard error + DEPEND_0: epoch + LABL_PTR_1: velocity_gse_label + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: Vp GSE Err + UNITS: km/s + VALIDMIN: 0 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear +proton_sw_velocity_gsm_sun: + NAME: proton_sw_velocity_gsm_sun + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity (GSM, Sun Frame) + CATDESC: Solar wind proton bulk velocity vector in GSM coordinates (Sun frame) + DEPEND_0: epoch + LABL_PTR_1: proton_sw_velocity_gsm_sun_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Vp GSM Sun + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: proton_sw_velocity_gsm_uncert + DELTA_MINUS_VAR: proton_sw_velocity_gsm_uncert +proton_sw_velocity_gsm: + NAME: proton_sw_velocity_gsm + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity (GSM) + CATDESC: Solar wind proton bulk velocity vector in GSM coordinates (spacecraft frame) + DEPEND_0: epoch + LABL_PTR_1: proton_sw_velocity_gsm_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Vp GSM SC + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: proton_sw_velocity_gsm_uncert + DELTA_MINUS_VAR: proton_sw_velocity_gsm_uncert +proton_sw_velocity_gsm_covariance: + NAME: proton_sw_velocity_gsm_covariance + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity Covariance (GSM) + CATDESC: Covariance matrix in GSM coordinates, with standard errors given by the square root of the diagonal + DEPEND_0: epoch + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: cov Vp + UNITS: km^2/s^2 + VALIDMIN: -1.0e+8 + VALIDMAX: 1.0e+8 + FILLVAL: -1.000000e+31 + LABL_PTR_1: velocity_gsm_label + LABL_PTR_2: velocity_gsm_label +proton_sw_velocity_gsm_uncert: + NAME: proton_sw_velocity_gsm_uncert + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity Uncertainty (GSM) + CATDESC: 1-sigma standard error + DEPEND_0: epoch + LABL_PTR_1: velocity_gsm_label + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: Vp GSM Err + UNITS: km/s + VALIDMIN: 0 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear +proton_sw_velocity_hae_sun: + NAME: proton_sw_velocity_hae_sun + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity (HAE, Sun Frame) + CATDESC: Solar wind proton bulk velocity vector in HAE coordinates (Sun frame) + DEPEND_0: epoch + LABL_PTR_1: proton_sw_velocity_hae_sun_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Vp HAE Sun + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: proton_sw_velocity_hae_uncert + DELTA_MINUS_VAR: proton_sw_velocity_hae_uncert +proton_sw_velocity_hae: + NAME: proton_sw_velocity_hae + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity (HAE) + CATDESC: Solar wind proton bulk velocity vector in HAE coordinates (spacecraft frame) + DEPEND_0: epoch + LABL_PTR_1: proton_sw_velocity_hae_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Vp HAE SC + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: proton_sw_velocity_hae_uncert + DELTA_MINUS_VAR: proton_sw_velocity_hae_uncert +proton_sw_velocity_hae_covariance: + NAME: proton_sw_velocity_hae_covariance + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity Covariance (HAE) + CATDESC: Covariance matrix in HAE coordinates, with standard errors given by the square root of the diagonal + DEPEND_0: epoch + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: cov Vp + UNITS: km^2/s^2 + VALIDMIN: -1.0e+8 + VALIDMAX: 1.0e+8 + FILLVAL: -1.000000e+31 + LABL_PTR_1: velocity_hae_label + LABL_PTR_2: velocity_hae_label +proton_sw_velocity_hae_uncert: + NAME: proton_sw_velocity_hae_uncert + DATA_TYPE: CDF_REAL4 + FIELDNAM: Proton Solar Wind Velocity Uncertainty (HAE) + CATDESC: 1-sigma standard error + DEPEND_0: epoch + LABL_PTR_1: velocity_hae_label + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: Vp HAE Err + UNITS: km/s + VALIDMIN: 0 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear alpha_sw_speed: NAME: alpha_sw_speed DATA_TYPE: CDF_REAL4 @@ -598,6 +814,222 @@ alpha_sw_velocity_rtn_uncert: VALIDMAX: 10000 FILLVAL: -1.000000e+31 SCALETYP: linear +alpha_sw_velocity_gse_sun: + NAME: alpha_sw_velocity_gse_sun + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity (GSE, Sun Frame) + CATDESC: Solar wind alpha-particle bulk velocity vector in GSE coordinates (Sun frame) + DEPEND_0: epoch + LABL_PTR_1: alpha_sw_velocity_gse_sun_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Va GSE Sun + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: alpha_sw_velocity_gse_uncert + DELTA_MINUS_VAR: alpha_sw_velocity_gse_uncert +alpha_sw_velocity_gse: + NAME: alpha_sw_velocity_gse + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity (GSE) + CATDESC: Solar wind alpha-particle bulk velocity vector in GSE coordinates (spacecraft frame) + DEPEND_0: epoch + LABL_PTR_1: alpha_sw_velocity_gse_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Va GSE SC + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: alpha_sw_velocity_gse_uncert + DELTA_MINUS_VAR: alpha_sw_velocity_gse_uncert +alpha_sw_velocity_gse_covariance: + NAME: alpha_sw_velocity_gse_covariance + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity Covariance (GSE) + CATDESC: Covariance matrix in GSE coordinates, with standard errors given by the square root of the diagonal + DEPEND_0: epoch + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: cov Va + UNITS: km^2/s^2 + VALIDMIN: -1.0e+8 + VALIDMAX: 1.0e+8 + FILLVAL: -1.000000e+31 + LABL_PTR_1: velocity_gse_label + LABL_PTR_2: velocity_gse_label +alpha_sw_velocity_gse_uncert: + NAME: alpha_sw_velocity_gse_uncert + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity Uncertainty (GSE) + CATDESC: 1-sigma standard error + DEPEND_0: epoch + LABL_PTR_1: velocity_gse_label + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: Va GSE Err + UNITS: km/s + VALIDMIN: 0 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear +alpha_sw_velocity_gsm_sun: + NAME: alpha_sw_velocity_gsm_sun + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity (GSM, Sun Frame) + CATDESC: Solar wind alpha-particle bulk velocity vector in GSM coordinates (Sun frame) + DEPEND_0: epoch + LABL_PTR_1: alpha_sw_velocity_gsm_sun_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Va GSM Sun + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: alpha_sw_velocity_gsm_uncert + DELTA_MINUS_VAR: alpha_sw_velocity_gsm_uncert +alpha_sw_velocity_gsm: + NAME: alpha_sw_velocity_gsm + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity (GSM) + CATDESC: Solar wind alpha-particle bulk velocity vector in GSM coordinates (spacecraft frame) + DEPEND_0: epoch + LABL_PTR_1: alpha_sw_velocity_gsm_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Va GSM SC + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: alpha_sw_velocity_gsm_uncert + DELTA_MINUS_VAR: alpha_sw_velocity_gsm_uncert +alpha_sw_velocity_gsm_covariance: + NAME: alpha_sw_velocity_gsm_covariance + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity Covariance (GSM) + CATDESC: Covariance matrix in GSM coordinates, with standard errors given by the square root of the diagonal + DEPEND_0: epoch + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: cov Va + UNITS: km^2/s^2 + VALIDMIN: -1.0e+8 + VALIDMAX: 1.0e+8 + FILLVAL: -1.000000e+31 + LABL_PTR_1: velocity_gsm_label + LABL_PTR_2: velocity_gsm_label +alpha_sw_velocity_gsm_uncert: + NAME: alpha_sw_velocity_gsm_uncert + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity Uncertainty (GSM) + CATDESC: 1-sigma standard error + DEPEND_0: epoch + LABL_PTR_1: velocity_gsm_label + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: Va GSM Err + UNITS: km/s + VALIDMIN: 0 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear +alpha_sw_velocity_hae_sun: + NAME: alpha_sw_velocity_hae_sun + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity (HAE, Sun Frame) + CATDESC: Solar wind alpha-particle bulk velocity vector in HAE coordinates (Sun frame) + DEPEND_0: epoch + LABL_PTR_1: alpha_sw_velocity_hae_sun_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Va HAE Sun + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: alpha_sw_velocity_hae_uncert + DELTA_MINUS_VAR: alpha_sw_velocity_hae_uncert +alpha_sw_velocity_hae: + NAME: alpha_sw_velocity_hae + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity (HAE) + CATDESC: Solar wind alpha-particle bulk velocity vector in HAE coordinates (spacecraft frame) + DEPEND_0: epoch + LABL_PTR_1: alpha_sw_velocity_hae_label + VAR_TYPE: data + RECORD_VARYING: RV + DISPLAY_TYPE: time_series + VARIABLE_PURPOSE: PRIMARY_VAR,SUMMARY + FORMAT: F19.3 + LABLAXIS: Va HAE SC + UNITS: km/s + VALIDMIN: -10000 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear + DELTA_PLUS_VAR: alpha_sw_velocity_hae_uncert + DELTA_MINUS_VAR: alpha_sw_velocity_hae_uncert +alpha_sw_velocity_hae_covariance: + NAME: alpha_sw_velocity_hae_covariance + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity Covariance (HAE) + CATDESC: Covariance matrix in HAE coordinates, with standard errors given by the square root of the diagonal + DEPEND_0: epoch + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: cov Va + UNITS: km^2/s^2 + VALIDMIN: -1.0e+8 + VALIDMAX: 1.0e+8 + FILLVAL: -1.000000e+31 + LABL_PTR_1: velocity_hae_label + LABL_PTR_2: velocity_hae_label +alpha_sw_velocity_hae_uncert: + NAME: alpha_sw_velocity_hae_uncert + DATA_TYPE: CDF_REAL4 + FIELDNAM: Alpha Solar Wind Velocity Uncertainty (HAE) + CATDESC: 1-sigma standard error + DEPEND_0: epoch + LABL_PTR_1: velocity_hae_label + VAR_TYPE: support_data + RECORD_VARYING: RV + FORMAT: F19.3 + LABLAXIS: Va HAE Err + UNITS: km/s + VALIDMIN: 0 + VALIDMAX: 10000 + FILLVAL: -1.000000e+31 + SCALETYP: linear swp_flags: NAME: swp_flags DATA_TYPE: CDF_UINT2 @@ -630,6 +1062,33 @@ velocity_rtn_label: RECORD_VARYING: NRV FORMAT: a20 FILLVAL: ' ' +velocity_gse_label: + NAME: velocity_gse_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity GSE Label + CATDESC: Label for Geocentric Solar Ecliptic (GSE) velocity components + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +velocity_gsm_label: + NAME: velocity_gsm_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity GSM Label + CATDESC: Label for Geocentric Solar Magnetospheric (GSM) velocity components + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +velocity_hae_label: + NAME: velocity_hae_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity HAE Label + CATDESC: Label for Heliocentric Aries Ecliptic (HAE) velocity components + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' alpha_sw_velocity_rtn_label: NAME: alpha_sw_velocity_rtn_label DATA_TYPE: CDF_CHAR @@ -666,4 +1125,111 @@ proton_sw_velocity_rtn_sun_label: RECORD_VARYING: NRV FORMAT: a20 FILLVAL: ' ' - +alpha_sw_velocity_gse_label: + NAME: alpha_sw_velocity_gse_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity GSE Label + CATDESC: Label for alpha solar wind velocity components in GSE coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +alpha_sw_velocity_gse_sun_label: + NAME: alpha_sw_velocity_gse_sun_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity GSE Label + CATDESC: Label for Sun-frame alpha solar wind velocity components in GSE coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +alpha_sw_velocity_gsm_label: + NAME: alpha_sw_velocity_gsm_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity GSM Label + CATDESC: Label for alpha solar wind velocity components in GSM coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +alpha_sw_velocity_gsm_sun_label: + NAME: alpha_sw_velocity_gsm_sun_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity GSM Label + CATDESC: Label for Sun-frame alpha solar wind velocity components in GSM coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +alpha_sw_velocity_hae_label: + NAME: alpha_sw_velocity_hae_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity HAE Label + CATDESC: Label for alpha solar wind velocity components in HAE coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +alpha_sw_velocity_hae_sun_label: + NAME: alpha_sw_velocity_hae_sun_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity HAE Label + CATDESC: Label for Sun-frame alpha solar wind velocity components in HAE coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +proton_sw_velocity_gse_label: + NAME: proton_sw_velocity_gse_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity GSE Label + CATDESC: Label for proton solar wind velocity components in GSE coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +proton_sw_velocity_gse_sun_label: + NAME: proton_sw_velocity_gse_sun_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity GSE Label + CATDESC: Label for Sun-frame proton solar wind velocity components in GSE coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +proton_sw_velocity_gsm_label: + NAME: proton_sw_velocity_gsm_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity GSM Label + CATDESC: Label for proton solar wind velocity components in GSM coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +proton_sw_velocity_gsm_sun_label: + NAME: proton_sw_velocity_gsm_sun_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity GSM Label + CATDESC: Label for Sun-frame proton solar wind velocity components in GSM coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +proton_sw_velocity_hae_label: + NAME: proton_sw_velocity_hae_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity HAE Label + CATDESC: Label for proton solar wind velocity components in HAE coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' +proton_sw_velocity_hae_sun_label: + NAME: proton_sw_velocity_hae_sun_label + DATA_TYPE: CDF_CHAR + FIELDNAM: Velocity HAE Label + CATDESC: Label for Sun-frame proton solar wind velocity components in HAE coordinates + VAR_TYPE: metadata + RECORD_VARYING: NRV + FORMAT: a20 + FILLVAL: ' ' diff --git a/imap_l3_processing/swapi/l3a/models.py b/imap_l3_processing/swapi/l3a/models.py index c04d82eb..23e5723e 100644 --- a/imap_l3_processing/swapi/l3a/models.py +++ b/imap_l3_processing/swapi/l3a/models.py @@ -34,6 +34,30 @@ PROTON_SOLAR_WIND_VELOCITY_RTN_UNCERTAINTY_CDF_VAR_NAME = ( "proton_sw_velocity_rtn_uncert" ) +PROTON_SOLAR_WIND_VELOCITY_GSE_SUN_CDF_VAR_NAME = "proton_sw_velocity_gse_sun" +PROTON_SOLAR_WIND_VELOCITY_GSE_CDF_VAR_NAME = "proton_sw_velocity_gse" +PROTON_SOLAR_WIND_VELOCITY_GSE_COVARIANCE_CDF_VAR_NAME = ( + "proton_sw_velocity_gse_covariance" +) +PROTON_SOLAR_WIND_VELOCITY_GSE_UNCERTAINTY_CDF_VAR_NAME = ( + "proton_sw_velocity_gse_uncert" +) +PROTON_SOLAR_WIND_VELOCITY_GSM_SUN_CDF_VAR_NAME = "proton_sw_velocity_gsm_sun" +PROTON_SOLAR_WIND_VELOCITY_GSM_CDF_VAR_NAME = "proton_sw_velocity_gsm" +PROTON_SOLAR_WIND_VELOCITY_GSM_COVARIANCE_CDF_VAR_NAME = ( + "proton_sw_velocity_gsm_covariance" +) +PROTON_SOLAR_WIND_VELOCITY_GSM_UNCERTAINTY_CDF_VAR_NAME = ( + "proton_sw_velocity_gsm_uncert" +) +PROTON_SOLAR_WIND_VELOCITY_HAE_SUN_CDF_VAR_NAME = "proton_sw_velocity_hae_sun" +PROTON_SOLAR_WIND_VELOCITY_HAE_CDF_VAR_NAME = "proton_sw_velocity_hae" +PROTON_SOLAR_WIND_VELOCITY_HAE_COVARIANCE_CDF_VAR_NAME = ( + "proton_sw_velocity_hae_covariance" +) +PROTON_SOLAR_WIND_VELOCITY_HAE_UNCERTAINTY_CDF_VAR_NAME = ( + "proton_sw_velocity_hae_uncert" +) ALPHA_SOLAR_WIND_SPEED_CDF_VAR_NAME = "alpha_sw_speed" ALPHA_SOLAR_WIND_SPEED_UNCERTAINTY_CDF_VAR_NAME = "alpha_sw_speed_uncert" @@ -51,6 +75,30 @@ ALPHA_SOLAR_WIND_VELOCITY_RTN_UNCERTAINTY_CDF_VAR_NAME = ( "alpha_sw_velocity_rtn_uncert" ) +ALPHA_SOLAR_WIND_VELOCITY_GSE_SUN_CDF_VAR_NAME = "alpha_sw_velocity_gse_sun" +ALPHA_SOLAR_WIND_VELOCITY_GSE_CDF_VAR_NAME = "alpha_sw_velocity_gse" +ALPHA_SOLAR_WIND_VELOCITY_GSE_COVARIANCE_CDF_VAR_NAME = ( + "alpha_sw_velocity_gse_covariance" +) +ALPHA_SOLAR_WIND_VELOCITY_GSE_UNCERTAINTY_CDF_VAR_NAME = ( + "alpha_sw_velocity_gse_uncert" +) +ALPHA_SOLAR_WIND_VELOCITY_GSM_SUN_CDF_VAR_NAME = "alpha_sw_velocity_gsm_sun" +ALPHA_SOLAR_WIND_VELOCITY_GSM_CDF_VAR_NAME = "alpha_sw_velocity_gsm" +ALPHA_SOLAR_WIND_VELOCITY_GSM_COVARIANCE_CDF_VAR_NAME = ( + "alpha_sw_velocity_gsm_covariance" +) +ALPHA_SOLAR_WIND_VELOCITY_GSM_UNCERTAINTY_CDF_VAR_NAME = ( + "alpha_sw_velocity_gsm_uncert" +) +ALPHA_SOLAR_WIND_VELOCITY_HAE_SUN_CDF_VAR_NAME = "alpha_sw_velocity_hae_sun" +ALPHA_SOLAR_WIND_VELOCITY_HAE_CDF_VAR_NAME = "alpha_sw_velocity_hae" +ALPHA_SOLAR_WIND_VELOCITY_HAE_COVARIANCE_CDF_VAR_NAME = ( + "alpha_sw_velocity_hae_covariance" +) +ALPHA_SOLAR_WIND_VELOCITY_HAE_UNCERTAINTY_CDF_VAR_NAME = ( + "alpha_sw_velocity_hae_uncert" +) PUI_IONIZATION_RATE_CDF_VAR_NAME = "pui_ionization_rate" PUI_CUTOFF_SPEED_CDF_VAR_NAME = "pui_cutoff_speed" @@ -64,11 +112,26 @@ SWAPI_QUALITY_FLAGS_CDF_VAR_NAME = "swp_flags" VELOCITY_RTN_LABEL_CDF_VAR_NAME = "velocity_rtn_label" +VELOCITY_GSE_LABEL_CDF_VAR_NAME = "velocity_gse_label" +VELOCITY_GSM_LABEL_CDF_VAR_NAME = "velocity_gsm_label" +VELOCITY_HAE_LABEL_CDF_VAR_NAME = "velocity_hae_label" ALPHA_VELOCITY_RTN_LABEL_CDF_VAR_NAME = "alpha_sw_velocity_rtn_label" ALPHA_VELOCITY_RTN_SUN_LABEL_CDF_VAR_NAME = "alpha_sw_velocity_rtn_sun_label" +ALPHA_VELOCITY_GSE_LABEL_CDF_VAR_NAME = "alpha_sw_velocity_gse_label" +ALPHA_VELOCITY_GSE_SUN_LABEL_CDF_VAR_NAME = "alpha_sw_velocity_gse_sun_label" +ALPHA_VELOCITY_GSM_LABEL_CDF_VAR_NAME = "alpha_sw_velocity_gsm_label" +ALPHA_VELOCITY_GSM_SUN_LABEL_CDF_VAR_NAME = "alpha_sw_velocity_gsm_sun_label" +ALPHA_VELOCITY_HAE_LABEL_CDF_VAR_NAME = "alpha_sw_velocity_hae_label" +ALPHA_VELOCITY_HAE_SUN_LABEL_CDF_VAR_NAME = "alpha_sw_velocity_hae_sun_label" PROTON_SOLAR_WIND_VELOCITY_RTN_SUN_LABEL_CDF_VAR_NAME = "proton_sw_velocity_rtn_sun_label" PROTON_SOLAR_WIND_VELOCITY_RTN_LABEL_CDF_VAR_NAME = "proton_sw_velocity_rtn_label" +PROTON_SOLAR_WIND_VELOCITY_GSE_SUN_LABEL_CDF_VAR_NAME = "proton_sw_velocity_gse_sun_label" +PROTON_SOLAR_WIND_VELOCITY_GSE_LABEL_CDF_VAR_NAME = "proton_sw_velocity_gse_label" +PROTON_SOLAR_WIND_VELOCITY_GSM_SUN_LABEL_CDF_VAR_NAME = "proton_sw_velocity_gsm_sun_label" +PROTON_SOLAR_WIND_VELOCITY_GSM_LABEL_CDF_VAR_NAME = "proton_sw_velocity_gsm_label" +PROTON_SOLAR_WIND_VELOCITY_HAE_SUN_LABEL_CDF_VAR_NAME = "proton_sw_velocity_hae_sun_label" +PROTON_SOLAR_WIND_VELOCITY_HAE_LABEL_CDF_VAR_NAME = "proton_sw_velocity_hae_label" @dataclass @@ -85,6 +148,15 @@ class SwapiL3ProtonSolarWindData(DataProduct): proton_sw_velocity_rtn_sun: np.ndarray # shape (N, 3), km/s, inertial RTN proton_sw_velocity_rtn: np.ndarray # shape (N, 3), km/s, RTN in SC rest frame proton_sw_velocity_rtn_covariance: np.ndarray # shape (N, 3, 3), km²/s² + proton_sw_velocity_gse_sun: np.ndarray + proton_sw_velocity_gse: np.ndarray + proton_sw_velocity_gse_covariance: np.ndarray + proton_sw_velocity_gsm_sun: np.ndarray + proton_sw_velocity_gsm: np.ndarray + proton_sw_velocity_gsm_covariance: np.ndarray + proton_sw_velocity_hae_sun: np.ndarray + proton_sw_velocity_hae: np.ndarray + proton_sw_velocity_hae_covariance: np.ndarray quality_flags: np.ndarray[SwapiL3Flags] def to_data_product_variables(self) -> list[DataProductVariable]: @@ -143,10 +215,79 @@ def to_data_product_variables(self) -> list[DataProductVariable]: ) ), ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_GSE_SUN_CDF_VAR_NAME, + self.proton_sw_velocity_gse_sun, + ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_GSE_CDF_VAR_NAME, + self.proton_sw_velocity_gse, + ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_GSE_COVARIANCE_CDF_VAR_NAME, + self.proton_sw_velocity_gse_covariance, + ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_GSE_UNCERTAINTY_CDF_VAR_NAME, + np.sqrt( + np.diagonal( + self.proton_sw_velocity_gse_covariance, axis1=1, axis2=2 + ) + ), + ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_GSM_SUN_CDF_VAR_NAME, + self.proton_sw_velocity_gsm_sun, + ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_GSM_CDF_VAR_NAME, + self.proton_sw_velocity_gsm, + ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_GSM_COVARIANCE_CDF_VAR_NAME, + self.proton_sw_velocity_gsm_covariance, + ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_GSM_UNCERTAINTY_CDF_VAR_NAME, + np.sqrt( + np.diagonal( + self.proton_sw_velocity_gsm_covariance, axis1=1, axis2=2 + ) + ), + ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_HAE_SUN_CDF_VAR_NAME, + self.proton_sw_velocity_hae_sun, + ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_HAE_CDF_VAR_NAME, + self.proton_sw_velocity_hae, + ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_HAE_COVARIANCE_CDF_VAR_NAME, + self.proton_sw_velocity_hae_covariance, + ), + DataProductVariable( + PROTON_SOLAR_WIND_VELOCITY_HAE_UNCERTAINTY_CDF_VAR_NAME, + np.sqrt( + np.diagonal( + self.proton_sw_velocity_hae_covariance, axis1=1, axis2=2 + ) + ), + ), DataProductVariable(SWAPI_QUALITY_FLAGS_CDF_VAR_NAME, self.quality_flags), DataProductVariable(VELOCITY_RTN_LABEL_CDF_VAR_NAME, value=["R", "T", "N"]), + DataProductVariable(VELOCITY_GSE_LABEL_CDF_VAR_NAME, value=["X", "Y", "Z"]), + DataProductVariable(VELOCITY_GSM_LABEL_CDF_VAR_NAME, value=["X", "Y", "Z"]), + DataProductVariable(VELOCITY_HAE_LABEL_CDF_VAR_NAME, value=["X", "Y", "Z"]), DataProductVariable(PROTON_SOLAR_WIND_VELOCITY_RTN_LABEL_CDF_VAR_NAME, value=["Vp SC R", "Vp SC T", "Vp SC N"]), DataProductVariable(PROTON_SOLAR_WIND_VELOCITY_RTN_SUN_LABEL_CDF_VAR_NAME, value=["Vp Sun R", "Vp Sun T", "Vp Sun N"]), + DataProductVariable(PROTON_SOLAR_WIND_VELOCITY_GSE_LABEL_CDF_VAR_NAME, value=["Vp SC X", "Vp SC Y", "Vp SC Z"]), + DataProductVariable(PROTON_SOLAR_WIND_VELOCITY_GSE_SUN_LABEL_CDF_VAR_NAME, value=["Vp Sun X", "Vp Sun Y", "Vp Sun Z"]), + DataProductVariable(PROTON_SOLAR_WIND_VELOCITY_GSM_LABEL_CDF_VAR_NAME, value=["Vp SC X", "Vp SC Y", "Vp SC Z"]), + DataProductVariable(PROTON_SOLAR_WIND_VELOCITY_GSM_SUN_LABEL_CDF_VAR_NAME, value=["Vp Sun X", "Vp Sun Y", "Vp Sun Z"]), + DataProductVariable(PROTON_SOLAR_WIND_VELOCITY_HAE_LABEL_CDF_VAR_NAME, value=["Vp SC X", "Vp SC Y", "Vp SC Z"]), + DataProductVariable(PROTON_SOLAR_WIND_VELOCITY_HAE_SUN_LABEL_CDF_VAR_NAME, value=["Vp Sun X", "Vp Sun Y", "Vp Sun Z"]), ] @@ -164,6 +305,15 @@ class SwapiL3AlphaSolarWindData(DataProduct): alpha_sw_velocity_rtn_sun: np.ndarray # shape (N, 3), km/s, inertial RTN alpha_sw_velocity_rtn: np.ndarray # shape (N, 3), km/s, RTN in SC rest frame alpha_sw_velocity_rtn_covariance: np.ndarray # shape (N, 3, 3), km²/s² + alpha_sw_velocity_gse_sun: np.ndarray + alpha_sw_velocity_gse: np.ndarray + alpha_sw_velocity_gse_covariance: np.ndarray + alpha_sw_velocity_gsm_sun: np.ndarray + alpha_sw_velocity_gsm: np.ndarray + alpha_sw_velocity_gsm_covariance: np.ndarray + alpha_sw_velocity_hae_sun: np.ndarray + alpha_sw_velocity_hae: np.ndarray + alpha_sw_velocity_hae_covariance: np.ndarray quality_flags: np.ndarray def to_data_product_variables(self) -> list[DataProductVariable]: @@ -222,10 +372,79 @@ def to_data_product_variables(self) -> list[DataProductVariable]: ) ), ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_GSE_SUN_CDF_VAR_NAME, + self.alpha_sw_velocity_gse_sun, + ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_GSE_CDF_VAR_NAME, + self.alpha_sw_velocity_gse, + ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_GSE_COVARIANCE_CDF_VAR_NAME, + self.alpha_sw_velocity_gse_covariance, + ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_GSE_UNCERTAINTY_CDF_VAR_NAME, + np.sqrt( + np.diagonal( + self.alpha_sw_velocity_gse_covariance, axis1=1, axis2=2 + ) + ), + ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_GSM_SUN_CDF_VAR_NAME, + self.alpha_sw_velocity_gsm_sun, + ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_GSM_CDF_VAR_NAME, + self.alpha_sw_velocity_gsm, + ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_GSM_COVARIANCE_CDF_VAR_NAME, + self.alpha_sw_velocity_gsm_covariance, + ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_GSM_UNCERTAINTY_CDF_VAR_NAME, + np.sqrt( + np.diagonal( + self.alpha_sw_velocity_gsm_covariance, axis1=1, axis2=2 + ) + ), + ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_HAE_SUN_CDF_VAR_NAME, + self.alpha_sw_velocity_hae_sun, + ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_HAE_CDF_VAR_NAME, + self.alpha_sw_velocity_hae, + ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_HAE_COVARIANCE_CDF_VAR_NAME, + self.alpha_sw_velocity_hae_covariance, + ), + DataProductVariable( + ALPHA_SOLAR_WIND_VELOCITY_HAE_UNCERTAINTY_CDF_VAR_NAME, + np.sqrt( + np.diagonal( + self.alpha_sw_velocity_hae_covariance, axis1=1, axis2=2 + ) + ), + ), DataProductVariable(SWAPI_QUALITY_FLAGS_CDF_VAR_NAME, self.quality_flags), DataProductVariable(VELOCITY_RTN_LABEL_CDF_VAR_NAME, value=["R", "T", "N"]), + DataProductVariable(VELOCITY_GSE_LABEL_CDF_VAR_NAME, value=["X", "Y", "Z"]), + DataProductVariable(VELOCITY_GSM_LABEL_CDF_VAR_NAME, value=["X", "Y", "Z"]), + DataProductVariable(VELOCITY_HAE_LABEL_CDF_VAR_NAME, value=["X", "Y", "Z"]), DataProductVariable(ALPHA_VELOCITY_RTN_LABEL_CDF_VAR_NAME, value=["Va SC R", "Va SC T", "Va SC N"]), DataProductVariable(ALPHA_VELOCITY_RTN_SUN_LABEL_CDF_VAR_NAME, value=["Va Sun R", "Va Sun T", "Va Sun N"]), + DataProductVariable(ALPHA_VELOCITY_GSE_LABEL_CDF_VAR_NAME, value=["Va SC X", "Va SC Y", "Va SC Z"]), + DataProductVariable(ALPHA_VELOCITY_GSE_SUN_LABEL_CDF_VAR_NAME, value=["Va Sun X", "Va Sun Y", "Va Sun Z"]), + DataProductVariable(ALPHA_VELOCITY_GSM_LABEL_CDF_VAR_NAME, value=["Va SC X", "Va SC Y", "Va SC Z"]), + DataProductVariable(ALPHA_VELOCITY_GSM_SUN_LABEL_CDF_VAR_NAME, value=["Va Sun X", "Va Sun Y", "Va Sun Z"]), + DataProductVariable(ALPHA_VELOCITY_HAE_LABEL_CDF_VAR_NAME, value=["Va SC X", "Va SC Y", "Va SC Z"]), + DataProductVariable(ALPHA_VELOCITY_HAE_SUN_LABEL_CDF_VAR_NAME, value=["Va Sun X", "Va Sun Y", "Va Sun Z"]), ] diff --git a/imap_l3_processing/swapi/l3a/utils.py b/imap_l3_processing/swapi/l3a/utils.py index 0f44b2f5..74ec89a7 100644 --- a/imap_l3_processing/swapi/l3a/utils.py +++ b/imap_l3_processing/swapi/l3a/utils.py @@ -115,6 +115,38 @@ def get_spacecraft_velocity_rtn(epoch_tt2000_ns: float) -> ndarray: return np.einsum("ij,j->i", rtn_from_eclipj2000, state_eclipj2000[3:]) +def convert_velocity_rtn_to_frame( + epoch_tt2000_ns: ndarray, + velocity_rtn: ndarray, + target_frame: SpiceFrame, +) -> ndarray: + """Express RTN velocity vectors in another coordinate frame.""" + ephemeris_times = ttj2000ns_to_et(epoch_tt2000_ns) + rotation_matrices = get_rotation_matrix( + ephemeris_times, SpiceFrame.IMAP_RTN, target_frame + ) + return np.einsum("nij,nj->ni", rotation_matrices, velocity_rtn) + + +def convert_velocity_covariance_rtn_to_frame( + epoch_tt2000_ns: ndarray, + covariance_rtn: ndarray, + target_frame: SpiceFrame, +) -> ndarray: + """Express RTN velocity covariance matrices in another coordinate frame.""" + ephemeris_times = ttj2000ns_to_et(epoch_tt2000_ns) + rotation_matrices = get_rotation_matrix( + ephemeris_times, SpiceFrame.IMAP_RTN, target_frame + ) + # This is equivalent to R C R^T + return np.einsum( + "nij,njk,nlk->nil", + rotation_matrices, + covariance_rtn, + rotation_matrices, + ) + + @numba.njit def velocity_components_to_angles_in_instrument_frame(vx: float, vy: float, vz: float): """Convert a Cartesian flow-direction velocity to (azimuth_deg, elevation_deg) diff --git a/imap_l3_processing/swapi/swapi_processor.py b/imap_l3_processing/swapi/swapi_processor.py index aa4ae7b1..8655ea92 100644 --- a/imap_l3_processing/swapi/swapi_processor.py +++ b/imap_l3_processing/swapi/swapi_processor.py @@ -3,6 +3,7 @@ import numpy as np from imap_data_access.processing_input import ProcessingInputCollection +from imap_processing.spice.geometry import SpiceFrame from uncertainties.unumpy import uarray from imap_l3_processing.constants import FIVE_MINUTES_IN_NANOSECONDS @@ -26,6 +27,8 @@ from imap_l3_processing.swapi.l3a.swapi_l3a_dependencies import SwapiL3ADependencies from imap_l3_processing.swapi.l3a.utils import ( chunk_l2_data, + convert_velocity_covariance_rtn_to_frame, + convert_velocity_rtn_to_frame, ) from imap_l3_processing.swapi.l3b.models import SwapiL3BCombinedVDF from imap_l3_processing.swapi.l3b.science.calculate_solar_wind_differential_flux import ( @@ -44,6 +47,70 @@ logger = logging.getLogger(__name__) +_VELOCITY_TARGET_FRAMES = ( + ("gse", SpiceFrame.IMAP_GSE), + ("gsm", SpiceFrame.IMAP_GSM), + ("hae", SpiceFrame.ECLIPJ2000), +) + + +def _convert_velocity_products_at_epoch( + result: dict[str, np.ndarray], + prefix: str, + index: int, + target_frame: SpiceFrame, +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + """Convert velocity, Sun-relative velocity, and covariance for one epoch.""" + epoch_slice = result["epoch"][index : index + 1] + velocity = convert_velocity_rtn_to_frame( + epoch_slice, + result[f"{prefix}_rtn"][index : index + 1], + target_frame, + )[0] + velocity_sun = convert_velocity_rtn_to_frame( + epoch_slice, + result[f"{prefix}_rtn_sun"][index : index + 1], + target_frame, + )[0] + covariance = convert_velocity_covariance_rtn_to_frame( + epoch_slice, + result[f"{prefix}_rtn_covariance"][index : index + 1], + target_frame, + )[0] + return velocity, velocity_sun, covariance + + +def _add_velocity_products_in_target_frames( + result: dict[str, np.ndarray], species: str +) -> None: + """Add velocity vectors and covariance in each requested output frame.""" + prefix = f"{species}_sw_velocity" + + for frame_name, target_frame in _VELOCITY_TARGET_FRAMES: + velocity = np.full(result[f"{prefix}_rtn"].shape, np.nan) + velocity_sun = np.full(result[f"{prefix}_rtn_sun"].shape, np.nan) + covariance = np.full(result[f"{prefix}_rtn_covariance"].shape, np.nan) + + for index, epoch in enumerate(result["epoch"]): + try: + converted_products = _convert_velocity_products_at_epoch( + result, prefix, index, target_frame + ) + except Exception: + logger.warning( + f"Error converting {species} velocity products at epoch " + f"{epoch} to {target_frame}; using fill values.", + exc_info=True, + ) + result["quality_flags"][index] |= int(SwapiL3Flags.FIT_ERROR) + continue + + velocity[index], velocity_sun[index], covariance[index] = converted_products + + result[f"{prefix}_{frame_name}"] = velocity + result[f"{prefix}_{frame_name}_sun"] = velocity_sun + result[f"{prefix}_{frame_name}_covariance"] = covariance + class SwapiProcessor(Processor): def __init__( @@ -86,9 +153,11 @@ def process_l3a_proton(self, data, dependencies) -> SwapiL3ProtonSolarWindData: dependencies.swapi_response, dependencies.efficiency_calibration_table ) + result = runner.run(chunks, ProtonChunkFitter()) + _add_velocity_products_in_target_frames(result, "proton") + return SwapiL3ProtonSolarWindData( - replace(self.input_metadata, descriptor="proton-sw"), - **runner.run(chunks, ProtonChunkFitter()), + replace(self.input_metadata, descriptor="proton-sw"), **result ) def process_l3a_alpha(self, data, dependencies) -> SwapiL3AlphaSolarWindData: @@ -105,7 +174,9 @@ def process_l3a_alpha(self, data, dependencies) -> SwapiL3AlphaSolarWindData: fitter = AlphaChunkFitter(dependencies.mag_data) result = runner.run(chunks, fitter) - + + _add_velocity_products_in_target_frames(result, "alpha") + if dependencies.mag_is_preliminary: result["quality_flags"] = result["quality_flags"] | int(SwapiL3Flags.PRELIMINARY_MAG) diff --git a/tests/integration/test_swapi_processor_integration.py b/tests/integration/test_swapi_processor_integration.py index a88c0caa..7c0a9e04 100644 --- a/tests/integration/test_swapi_processor_integration.py +++ b/tests/integration/test_swapi_processor_integration.py @@ -34,6 +34,46 @@ SWAPI_INTEGRATION_DATA_DIR = Path(__file__).parent / "test_data" / "swapi" +def assert_velocity_frames_are_physically_consistent(test_case, cdf, prefix, index): + """Check the transformed vectors against the geometry expected on January 1.""" + for sun_suffix in ("", "_sun"): + rtn = cdf[f"{prefix}_rtn{sun_suffix}"][index] + gse = cdf[f"{prefix}_gse{sun_suffix}"][index] + gsm = cdf[f"{prefix}_gsm{sun_suffix}"][index] + hae = cdf[f"{prefix}_hae{sun_suffix}"][index] + + # A rotation changes coordinates, not the speed. + for transformed in (gse, gsm, hae): + numpy.testing.assert_allclose( + numpy.linalg.norm(transformed), + numpy.linalg.norm(rtn), + rtol=1e-6, + ) + + # GSE +X points from Earth toward the Sun, opposite the predominantly + # anti-sunward solar-wind flow. GSM is a rotation about that same X axis. + test_case.assertLess(gse[0], 0) + test_case.assertGreater(abs(gse[0]), numpy.linalg.norm(gse[1:])) + numpy.testing.assert_allclose(gsm[0], gse[0], rtol=1e-6) + + # Near January 1, Earth's anti-sunward radial direction in HAE is + # predominantly +Y (Earth's heliocentric ecliptic longitude is ~100 deg). + test_case.assertGreater(hae[1], 0) + test_case.assertGreater(abs(hae[1]), abs(hae[0])) + test_case.assertGreater(abs(hae[1]), abs(hae[2])) + + rtn_covariance = cdf[f"{prefix}_rtn_covariance"][index] + for frame in ("gse", "gsm", "hae"): + covariance = cdf[f"{prefix}_{frame}_covariance"][index] + numpy.testing.assert_allclose(covariance, covariance.T, rtol=1e-6, atol=1e-6) + numpy.testing.assert_allclose( + numpy.linalg.eigvalsh(covariance), + numpy.linalg.eigvalsh(rtn_covariance), + rtol=1e-5, + atol=1e-5, + ) + + class SwapiProcessorIntegration(unittest.TestCase): @skipUnless(os.environ.get("IMAP_API_KEY"), "requires production API key") def test_proton_sw_with_production_data(self): @@ -55,6 +95,12 @@ def test_proton_sw_with_production_data(self): 'proton_sw_density_uncert': 0.049651436507701874, 'proton_sw_velocity_rtn_sun': [474.1429748535156, 31.2506103515625, 15.421923637390137], 'proton_sw_velocity_rtn': [474.2013854980469, 1.6974985599517822, 18.847949981689453], + 'proton_sw_velocity_gse': [-474.21169142, -0.48161806, 18.65798744], + 'proton_sw_velocity_gse_sun': [-474.09292141, -30.23247255, 18.65198256], + 'proton_sw_velocity_gsm': [-474.21169142, -5.07181642, 17.96187990], + 'proton_sw_velocity_gsm_sun': [-474.09292141, -33.90076411, 10.61306001], + 'proton_sw_velocity_hae': [-84.52581716, 466.61913283, 18.62932749], + 'proton_sw_velocity_hae_sun': [-113.78456481, 461.22903633, 18.62379766], 'proton_sw_velocity_rtn_covariance': [[0.11774054169654846, -0.029444590210914612, 0.13579009473323822], [-0.029444590210914612, 0.8430156111717224, -0.22263257205486298], [0.13579009473323822, -0.22263257205486298, 1.3417274951934814]], @@ -111,6 +157,9 @@ def test_proton_sw_with_production_data(self): ) except TypeError: self.assertEqual(expected_values[key], actual_value, msg=key) + assert_velocity_frames_are_physically_consistent( + self, cdf, "proton_sw_velocity", 0 + ) @@ -139,6 +188,12 @@ def test_alpha_sw_with_production_data(self): 'alpha_sw_temperature_uncert': 1141809.75, 'alpha_sw_velocity_rtn': [473.010009765625, -9.928609848022461, 16.30108070373535], 'alpha_sw_velocity_rtn_sun': [472.9515686035156, 19.624502182006836, 12.875075340270996], + 'alpha_sw_velocity_gse': [-473.04103169, 10.77708246, 14.79218361], + 'alpha_sw_velocity_gse_sun': [-472.92223443, -18.97376979, 14.78618060], + 'alpha_sw_velocity_gsm': [-473.04103169, 6.79540805, 16.99342908], + 'alpha_sw_velocity_gsm_sun': [-472.92223443, -22.03470465, 9.64919000], + 'alpha_sw_velocity_hae': [-73.24367583, 467.46141800, 14.76341745], + 'alpha_sw_velocity_hae_sun': [-102.50234989, 462.07093380, 14.75788951], 'alpha_sw_velocity_rtn_covariance': [ [0.13386771082878113, 0.23742027580738068, 0.1489003300666809], [0.23742027580738068, 13.923757553100586, 7.167418003082275], @@ -196,6 +251,9 @@ def test_alpha_sw_with_production_data(self): ) except TypeError: self.assertEqual(expected_values[key], actual_value, msg=key) + assert_velocity_frames_are_physically_consistent( + self, cdf, "alpha_sw_velocity", sample_index + ) @skipUnless(os.environ.get("IMAP_API_KEY"), "requires production API key") diff --git a/tests/swapi/l3a/test_chunk_fits.py b/tests/swapi/l3a/test_chunk_fits.py index 6f06e265..d88c45f0 100644 --- a/tests/swapi/l3a/test_chunk_fits.py +++ b/tests/swapi/l3a/test_chunk_fits.py @@ -974,11 +974,12 @@ def test_bad_fit_when_alpha_bump_does_not_match_maxwellian(self): self.assertEqual(int(result["quality_flags"]), int(SwapiL3Flags.BAD_FIT)) _assert_all_nan(self, result, _ALPHA_SCALAR_KEYS, _ALPHA_ARRAY_KEYS) + @patch("imap_l3_processing.swapi.swapi_processor._add_velocity_products_in_target_frames") @patch("imap_l3_processing.swapi.swapi_processor.SwapiL3AlphaSolarWindData") @patch("imap_l3_processing.swapi.swapi_processor.ParallelChunkRunner") @patch("imap_l3_processing.swapi.swapi_processor.chunk_l2_data") def test_preliminary_mag_bit_set_iff_mag_is_preliminary( - self, mock_chunk_l2_data, mock_runner_class, mock_alpha_data_class + self, mock_chunk_l2_data, mock_runner_class, mock_alpha_data_class, _ ): """The `PRELIMINARY_MAG` bit is OR'd onto every per-chunk quality flag when `mag_is_preliminary=True` (preserving any underlying `BAD_FIT`), and the per-chunk flags pass through untouched when `mag_is_preliminary=False`. Mocked at the processor seam because the bit-OR is processor-level wiring, not chunk-fitter behavior.""" runner_flag = int(SwapiL3Flags.BAD_FIT) diff --git a/tests/swapi/l3a/test_models.py b/tests/swapi/l3a/test_models.py index 688414e2..00ef7f94 100644 --- a/tests/swapi/l3a/test_models.py +++ b/tests/swapi/l3a/test_models.py @@ -39,6 +39,20 @@ from tests.swapi.cdf_model_test_case import CdfModelTestCase from tests.test_helpers import with_tempdir +_FRAME_ROTATIONS = { + "gse": np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]]), + "gsm": np.array([[0, 0, 1], [-1, 0, 0], [0, -1, 0]]), + "hae": np.array([[-1, 0, 0], [0, 0, 1], [0, 1, 0]]), +} + + +def _rotate_velocity_frame(velocity_sun, velocity_sc, covariance, rotation): + return ( + velocity_sun @ rotation.T, + velocity_sc @ rotation.T, + rotation @ covariance @ rotation.T, + ) + class TestModels(CdfModelTestCase): @@ -58,6 +72,17 @@ def test_getting_proton_sw_data_product_variables(self): bulk_v_rtn_sun = np.arange(n * 3, dtype=float).reshape(n, 3) bulk_v_rtn_sc = np.arange(100, 100 + n * 3, dtype=float).reshape(n, 3) bulk_v_rtn_cov = np.arange(200, 200 + n * 9, dtype=float).reshape(n, 3, 3) + velocity_frames = { + "gse": _rotate_velocity_frame( + bulk_v_rtn_sun, bulk_v_rtn_sc, bulk_v_rtn_cov, _FRAME_ROTATIONS["gse"] + ), + "gsm": _rotate_velocity_frame( + bulk_v_rtn_sun, bulk_v_rtn_sc, bulk_v_rtn_cov, _FRAME_ROTATIONS["gsm"] + ), + "hae": _rotate_velocity_frame( + bulk_v_rtn_sun, bulk_v_rtn_sc, bulk_v_rtn_cov, _FRAME_ROTATIONS["hae"] + ), + } quality_flags = np.full(n, SwapiL3Flags.NONE) quality_flags[3:5] |= SwapiL3Flags.FIT_ERROR @@ -69,6 +94,9 @@ def test_getting_proton_sw_data_product_variables(self): temperature, temperature_uncert, density, density_uncert, bulk_v_rtn_sun, bulk_v_rtn_sc, bulk_v_rtn_cov, + *velocity_frames["gse"], + *velocity_frames["gsm"], + *velocity_frames["hae"], quality_flags, ) @@ -98,10 +126,30 @@ def test_getting_proton_sw_data_product_variables(self): expected_velocity_uncert = np.sqrt(np.diagonal(bulk_v_rtn_cov, axis1=1, axis2=2)) self.assert_variable_attributes(next(var_iter), expected_velocity_uncert, PROTON_SOLAR_WIND_VELOCITY_RTN_UNCERTAINTY_CDF_VAR_NAME) + for frame, (velocity_sun, velocity_sc, covariance) in velocity_frames.items(): + self.assert_variable_attributes(next(var_iter), velocity_sun, + f"proton_sw_velocity_{frame}_sun") + self.assert_variable_attributes(next(var_iter), velocity_sc, + f"proton_sw_velocity_{frame}") + self.assert_variable_attributes(next(var_iter), covariance, + f"proton_sw_velocity_{frame}_covariance") + self.assert_variable_attributes( + next(var_iter), + np.sqrt(np.diagonal(covariance, axis1=1, axis2=2)), + f"proton_sw_velocity_{frame}_uncert", + ) self.assert_variable_attributes(next(var_iter), quality_flags, SWAPI_QUALITY_FLAGS_CDF_VAR_NAME) self.assert_variable_attributes(next(var_iter), ["R", "T", "N"], VELOCITY_RTN_LABEL_CDF_VAR_NAME) + for frame in velocity_frames: + self.assert_variable_attributes(next(var_iter), ["X", "Y", "Z"], + f"velocity_{frame}_label") self.assert_variable_attributes(next(var_iter), ["Vp SC R", "Vp SC T", "Vp SC N"], PROTON_SOLAR_WIND_VELOCITY_RTN_LABEL_CDF_VAR_NAME) self.assert_variable_attributes(next(var_iter), ["Vp Sun R", "Vp Sun T", "Vp Sun N"], PROTON_SOLAR_WIND_VELOCITY_RTN_SUN_LABEL_CDF_VAR_NAME) + for frame in velocity_frames: + self.assert_variable_attributes(next(var_iter), ["Vp SC X", "Vp SC Y", "Vp SC Z"], + f"proton_sw_velocity_{frame}_label") + self.assert_variable_attributes(next(var_iter), ["Vp Sun X", "Vp Sun Y", "Vp Sun Z"], + f"proton_sw_velocity_{frame}_sun_label") self.assertIs(sentinel.done, next(var_iter, sentinel.done)) @@ -121,6 +169,17 @@ def test_getting_alpha_sw_data_product_variables(self): velocity_rtn_sun = np.arange(n * 3, dtype=float).reshape(n, 3) velocity_rtn_sc = np.arange(100, 100 + n * 3, dtype=float).reshape(n, 3) velocity_rtn_cov = np.arange(n * 9, dtype=float).reshape(n, 3, 3) + velocity_frames = { + "gse": _rotate_velocity_frame( + velocity_rtn_sun, velocity_rtn_sc, velocity_rtn_cov, _FRAME_ROTATIONS["gse"] + ), + "gsm": _rotate_velocity_frame( + velocity_rtn_sun, velocity_rtn_sc, velocity_rtn_cov, _FRAME_ROTATIONS["gsm"] + ), + "hae": _rotate_velocity_frame( + velocity_rtn_sun, velocity_rtn_sc, velocity_rtn_cov, _FRAME_ROTATIONS["hae"] + ), + } quality_flags = np.full_like(epoch_data, SwapiL3Flags.NONE) quality_flags[:n // 2] = SwapiL3Flags.BAD_FIT @@ -132,6 +191,9 @@ def test_getting_alpha_sw_data_product_variables(self): density, density_uncert, temperature, temperature_uncert, velocity_rtn_sun, velocity_rtn_sc, velocity_rtn_cov, + *velocity_frames["gse"], + *velocity_frames["gsm"], + *velocity_frames["hae"], quality_flags, ) variables = data.to_data_product_variables() @@ -160,10 +222,30 @@ def test_getting_alpha_sw_data_product_variables(self): expected_velocity_uncert = np.sqrt(np.diagonal(velocity_rtn_cov, axis1=1, axis2=2)) self.assert_variable_attributes(next(var_iter), expected_velocity_uncert, ALPHA_SOLAR_WIND_VELOCITY_RTN_UNCERTAINTY_CDF_VAR_NAME) + for frame, (velocity_sun, velocity_sc, covariance) in velocity_frames.items(): + self.assert_variable_attributes(next(var_iter), velocity_sun, + f"alpha_sw_velocity_{frame}_sun") + self.assert_variable_attributes(next(var_iter), velocity_sc, + f"alpha_sw_velocity_{frame}") + self.assert_variable_attributes(next(var_iter), covariance, + f"alpha_sw_velocity_{frame}_covariance") + self.assert_variable_attributes( + next(var_iter), + np.sqrt(np.diagonal(covariance, axis1=1, axis2=2)), + f"alpha_sw_velocity_{frame}_uncert", + ) self.assert_variable_attributes(next(var_iter), quality_flags, SWAPI_QUALITY_FLAGS_CDF_VAR_NAME) self.assert_variable_attributes(next(var_iter), ["R", "T", "N"], VELOCITY_RTN_LABEL_CDF_VAR_NAME) + for frame in velocity_frames: + self.assert_variable_attributes(next(var_iter), ["X", "Y", "Z"], + f"velocity_{frame}_label") self.assert_variable_attributes(next(var_iter), ['Va SC R', 'Va SC T', 'Va SC N'], ALPHA_VELOCITY_RTN_LABEL_CDF_VAR_NAME) self.assert_variable_attributes(next(var_iter), ['Va Sun R', 'Va Sun T', 'Va Sun N'], ALPHA_VELOCITY_RTN_SUN_LABEL_CDF_VAR_NAME) + for frame in velocity_frames: + self.assert_variable_attributes(next(var_iter), ["Va SC X", "Va SC Y", "Va SC Z"], + f"alpha_sw_velocity_{frame}_label") + self.assert_variable_attributes(next(var_iter), ["Va Sun X", "Va Sun Y", "Va Sun Z"], + f"alpha_sw_velocity_{frame}_sun_label") self.assertIs(sentinel.done, next(var_iter, sentinel.done)) diff --git a/tests/swapi/l3a/test_utils.py b/tests/swapi/l3a/test_utils.py index 02ea4897..790e4cae 100644 --- a/tests/swapi/l3a/test_utils.py +++ b/tests/swapi/l3a/test_utils.py @@ -2,9 +2,11 @@ from datetime import datetime from pathlib import Path from unittest import TestCase +from unittest.mock import patch import numpy as np import spacepy.pycdf +from imap_processing.spice.geometry import SpiceFrame from spacepy.pycdf import CDF from uncertainties import UFloat, ufloat @@ -25,6 +27,8 @@ ) from imap_l3_processing.swapi.l3a.models import SwapiL2Data from imap_l3_processing.swapi.l3a.utils import ( + convert_velocity_covariance_rtn_to_frame, + convert_velocity_rtn_to_frame, velocity_components_to_angles_in_instrument_frame, calculate_sw_speed, chunk_l2_data, @@ -299,6 +303,45 @@ def test_get_spacecraft_velocity_rtn_returns_finite_orbital_velocity(self): self.assertGreater(speed, 10.0) self.assertLess(speed, 60.0) + @patch("imap_l3_processing.swapi.l3a.utils.get_rotation_matrix") + @patch("imap_l3_processing.swapi.l3a.utils.ttj2000ns_to_et") + def test_convert_velocity_and_covariance_rtn_to_frame( + self, mock_time_conversion, mock_get_rotation_matrix + ): + epochs = np.array([1, 2]) + ephemeris_times = np.array([3.0, 4.0]) + rotations = np.array( + [ + np.eye(3), + [[0.0, -1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]], + ] + ) + velocities = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) + covariances = np.array( + [ + np.diag([1.0, 2.0, 3.0]), + [[4.0, 1.0, -2.0], [1.0, 5.0, 3.0], [-2.0, 3.0, 6.0]], + ] + ) + mock_time_conversion.return_value = ephemeris_times + mock_get_rotation_matrix.return_value = rotations + + actual_velocities = convert_velocity_rtn_to_frame( + epochs, velocities, SpiceFrame.IMAP_GSE + ) + actual_covariances = convert_velocity_covariance_rtn_to_frame( + epochs, covariances, SpiceFrame.IMAP_GSE + ) + + expected_velocities = (rotations @ velocities[..., np.newaxis])[..., 0] + expected_covariances = rotations @ covariances @ rotations.swapaxes(1, 2) + np.testing.assert_allclose(actual_velocities, expected_velocities) + np.testing.assert_allclose(actual_covariances, expected_covariances) + self.assertEqual(2, mock_get_rotation_matrix.call_count) + mock_get_rotation_matrix.assert_called_with( + ephemeris_times, SpiceFrame.IMAP_RTN, SpiceFrame.IMAP_GSE + ) + class TestVelocityComponentsToAnglesInInstrumentFrame(TestCase): """Tests for `velocity_components_to_angles_in_instrument_frame`. The Cartesian input diff --git a/tests/swapi/test_swapi_processor.py b/tests/swapi/test_swapi_processor.py index d06a97d5..f05d95c6 100644 --- a/tests/swapi/test_swapi_processor.py +++ b/tests/swapi/test_swapi_processor.py @@ -23,11 +23,75 @@ from imap_l3_processing.swapi.l3b.science.calculate_solar_wind_vdf import DeltaMinusPlus from imap_l3_processing.swapi.quality_flags import SwapiL3Flags from imap_l3_processing.swapi.species import Species -from imap_l3_processing.swapi.swapi_processor import SwapiProcessor +from imap_l3_processing.swapi.swapi_processor import ( + SwapiProcessor, + _add_velocity_products_in_target_frames, +) from tests.test_helpers import create_mock_version_map class TestSwapiProcessor(TestCase): + @patch( + "imap_l3_processing.swapi.swapi_processor.convert_velocity_covariance_rtn_to_frame" + ) + @patch("imap_l3_processing.swapi.swapi_processor.convert_velocity_rtn_to_frame") + def test_target_frame_conversion_fills_and_flags_only_failed_epoch( + self, mock_convert_velocity, mock_convert_covariance + ): + result = { + "epoch": np.array([1, 2, 3]), + "proton_sw_velocity_rtn": np.ones((3, 3)), + "proton_sw_velocity_rtn_sun": np.full((3, 3), 2.0), + "proton_sw_velocity_rtn_covariance": np.repeat( + np.eye(3)[np.newaxis, ...], 3, axis=0 + ), + "quality_flags": np.array( + [SwapiL3Flags.NONE, SwapiL3Flags.BAD_FIT, SwapiL3Flags.NONE] + ), + } + + def convert_velocity(epoch, value, _target_frame): + if epoch[0] == 2: + raise RuntimeError("conversion failed") + return value * 10 + + mock_convert_velocity.side_effect = convert_velocity + mock_convert_covariance.side_effect = lambda epoch, value, frame: value * 10 + + with self.assertLogs( + "imap_l3_processing.swapi.swapi_processor", level="WARNING" + ): + _add_velocity_products_in_target_frames(result, "proton") + + for frame in ("gse", "gsm", "hae"): + np.testing.assert_array_equal( + result[f"proton_sw_velocity_{frame}"][[0, 2]], 10 + ) + np.testing.assert_array_equal( + result[f"proton_sw_velocity_{frame}_sun"][[0, 2]], 20 + ) + np.testing.assert_array_equal( + result[f"proton_sw_velocity_{frame}_covariance"][[0, 2]], + np.repeat((np.eye(3) * 10)[np.newaxis, ...], 2, axis=0), + ) + self.assertTrue(np.all(np.isnan(result[f"proton_sw_velocity_{frame}"][1]))) + self.assertTrue( + np.all(np.isnan(result[f"proton_sw_velocity_{frame}_sun"][1])) + ) + self.assertTrue( + np.all( + np.isnan(result[f"proton_sw_velocity_{frame}_covariance"][1]) + ) + ) + np.testing.assert_array_equal( + result["quality_flags"], + [ + SwapiL3Flags.NONE, + SwapiL3Flags.BAD_FIT | SwapiL3Flags.FIT_ERROR, + SwapiL3Flags.NONE, + ], + ) + @patch('imap_l3_processing.utils.ImapAttributeManager') @patch('imap_l3_processing.swapi.swapi_processor.SwapiL3PickupIonData') @patch('imap_l3_processing.utils.write_cdf') @@ -192,6 +256,8 @@ def test_process_l3a_pui(self, mock_spicepy, mock_write_cdf.assert_called_once_with(str(expected_cdf_path), pickup_ion_data, mock_manager) self.assertEqual([expected_cdf_path], product) + @patch('imap_l3_processing.swapi.swapi_processor.convert_velocity_covariance_rtn_to_frame') + @patch('imap_l3_processing.swapi.swapi_processor.convert_velocity_rtn_to_frame') @patch('imap_l3_processing.utils.ImapAttributeManager') @patch('imap_l3_processing.swapi.swapi_processor.SwapiL3ProtonSolarWindData') @patch('imap_l3_processing.utils.write_cdf') @@ -205,7 +271,9 @@ def test_process_l3a_proton(self, mock_spicepy, mock_parallel_chunk_runner_class, mock_write_cdf, mock_proton_solar_wind_data_constructor, - mock_imap_attribute_manager): + mock_imap_attribute_manager, + mock_convert_velocity, + mock_convert_covariance): instrument = 'swapi' incoming_data_level = 'l2' dependency_start_date = datetime.strftime(datetime(2025, 1, 1), "%Y%m%d") @@ -257,6 +325,8 @@ def test_process_l3a_proton(self, mock_spicepy, ) mock_runner = mock_parallel_chunk_runner_class.return_value mock_runner.run.return_value = runner_result + mock_convert_velocity.side_effect = lambda epoch, value, frame: value + mock_convert_covariance.side_effect = lambda epoch, value, frame: value input_file_names = [ f'imap_{instrument}_{incoming_data_level}_{SWAPI_L2_DESCRIPTOR}_{dependency_start_date}_{version}.cdf', @@ -313,6 +383,12 @@ def test_process_l3a_proton(self, mock_spicepy, self.assertEqual(expected_proton_metadata, actual_positional[0]) for key, expected_val in runner_result.items(): np.testing.assert_array_equal(expected_val, actual_kwargs[key]) + for frame in ("gse", "gsm", "hae"): + self.assertIn(f"proton_sw_velocity_{frame}", actual_kwargs) + self.assertIn(f"proton_sw_velocity_{frame}_sun", actual_kwargs) + self.assertIn(f"proton_sw_velocity_{frame}_covariance", actual_kwargs) + self.assertEqual(6, mock_convert_velocity.call_count) + self.assertEqual(3, mock_convert_covariance.call_count) mock_manager.add_global_attribute.assert_has_calls([call("Data_version", outgoing_version), call("Generation_date", @@ -329,6 +405,8 @@ def test_process_l3a_proton(self, mock_spicepy, mock_write_cdf.assert_called_once_with(str(expected_cdf_path), proton_solar_wind_data, mock_manager) self.assertEqual([expected_cdf_path], product) + @patch('imap_l3_processing.swapi.swapi_processor.convert_velocity_covariance_rtn_to_frame') + @patch('imap_l3_processing.swapi.swapi_processor.convert_velocity_rtn_to_frame') @patch('imap_l3_processing.utils.ImapAttributeManager') @patch('imap_l3_processing.swapi.swapi_processor.SwapiL3AlphaSolarWindData') @patch('imap_l3_processing.utils.write_cdf') @@ -342,7 +420,9 @@ def test_process_l3a_alpha(self, mock_spicepy, mock_parallel_chunk_runner_class, mock_write_cdf, mock_alpha_solar_wind_data_constructor, - mock_imap_attribute_manager): + mock_imap_attribute_manager, + mock_convert_velocity, + mock_convert_covariance): instrument = 'swapi' incoming_data_level = 'l2' dependency_start_date = datetime.strftime(datetime(2025, 1, 1), "%Y%m%d") @@ -384,6 +464,8 @@ def test_process_l3a_alpha(self, mock_spicepy, ) mock_runner = mock_parallel_chunk_runner_class.return_value mock_runner.run.return_value = runner_result + mock_convert_velocity.side_effect = lambda epoch, value, frame: value + mock_convert_covariance.side_effect = lambda epoch, value, frame: value science_input = ScienceInput( f'imap_{instrument}_{incoming_data_level}_{SWAPI_L2_DESCRIPTOR}_{dependency_start_date}_{version}.cdf') @@ -440,6 +522,12 @@ def test_process_l3a_alpha(self, mock_spicepy, self.assertEqual(expected_alpha_metadata, actual_positional[0]) for key, expected_val in runner_result.items(): np.testing.assert_array_equal(expected_val, actual_kwargs[key]) + for frame in ("gse", "gsm", "hae"): + self.assertIn(f"alpha_sw_velocity_{frame}", actual_kwargs) + self.assertIn(f"alpha_sw_velocity_{frame}_sun", actual_kwargs) + self.assertIn(f"alpha_sw_velocity_{frame}_covariance", actual_kwargs) + self.assertEqual(6, mock_convert_velocity.call_count) + self.assertEqual(3, mock_convert_covariance.call_count) mock_manager.add_global_attribute.assert_has_calls([ call("Data_version", outgoing_version), diff --git a/tests/test_utils.py b/tests/test_utils.py index dfbed681..eb251606 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -79,6 +79,15 @@ def test_save_data(self, mock_science_file_path_class, mock_write_cdf, mock_toda alpha_sw_velocity_rtn_sun=np.zeros((3, 3)), alpha_sw_velocity_rtn=np.zeros((3, 3)), alpha_sw_velocity_rtn_covariance=np.zeros((3, 3, 3)), + alpha_sw_velocity_gse_sun=np.zeros((3, 3)), + alpha_sw_velocity_gse=np.zeros((3, 3)), + alpha_sw_velocity_gse_covariance=np.zeros((3, 3, 3)), + alpha_sw_velocity_gsm_sun=np.zeros((3, 3)), + alpha_sw_velocity_gsm=np.zeros((3, 3)), + alpha_sw_velocity_gsm_covariance=np.zeros((3, 3, 3)), + alpha_sw_velocity_hae_sun=np.zeros((3, 3)), + alpha_sw_velocity_hae=np.zeros((3, 3)), + alpha_sw_velocity_hae_covariance=np.zeros((3, 3, 3)), parent_file_names=sentinel.parent_files, quality_flags=sentinel.quality_flags, ) @@ -234,6 +243,15 @@ def test_save_data_does_not_add_parent_attribute_if_empty(self, mock_write_cdf, alpha_sw_velocity_rtn_sun=np.zeros((3, 3)), alpha_sw_velocity_rtn=np.zeros((3, 3)), alpha_sw_velocity_rtn_covariance=np.zeros((3, 3, 3)), + alpha_sw_velocity_gse_sun=np.zeros((3, 3)), + alpha_sw_velocity_gse=np.zeros((3, 3)), + alpha_sw_velocity_gse_covariance=np.zeros((3, 3, 3)), + alpha_sw_velocity_gsm_sun=np.zeros((3, 3)), + alpha_sw_velocity_gsm=np.zeros((3, 3)), + alpha_sw_velocity_gsm_covariance=np.zeros((3, 3, 3)), + alpha_sw_velocity_hae_sun=np.zeros((3, 3)), + alpha_sw_velocity_hae=np.zeros((3, 3)), + alpha_sw_velocity_hae_covariance=np.zeros((3, 3, 3)), quality_flags=quality_flags, ) save_data(data_product) @@ -274,6 +292,15 @@ def test_save_data_custom_path(self, mock_write_cdf, mock_today, _): alpha_sw_velocity_rtn_sun=np.zeros((3, 3)), alpha_sw_velocity_rtn=np.zeros((3, 3)), alpha_sw_velocity_rtn_covariance=np.zeros((3, 3, 3)), + alpha_sw_velocity_gse_sun=np.zeros((3, 3)), + alpha_sw_velocity_gse=np.zeros((3, 3)), + alpha_sw_velocity_gse_covariance=np.zeros((3, 3, 3)), + alpha_sw_velocity_gsm_sun=np.zeros((3, 3)), + alpha_sw_velocity_gsm=np.zeros((3, 3)), + alpha_sw_velocity_gsm_covariance=np.zeros((3, 3, 3)), + alpha_sw_velocity_hae_sun=np.zeros((3, 3)), + alpha_sw_velocity_hae=np.zeros((3, 3)), + alpha_sw_velocity_hae_covariance=np.zeros((3, 3, 3)), quality_flags=quality_flags, )