Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
100 changes: 97 additions & 3 deletions imap_processing/ena_maps/utils/naming.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ class MappableInstrumentShortName(Enum):
"hk": "heliocentric kinetic",
}

# The principal data part of a descriptor is a stem naming the quantity mapped,
# optionally followed by modifier codes saying which corrections were made.
PRINCIPAL_DATA_PATTERN = re.compile(
r"^(drt|ena|int|isn|spx)(?:(?<=spx)\d+)?([^-_\s]*)$"
)


@dataclass
class MapDescriptor:
Expand Down Expand Up @@ -243,9 +249,7 @@ def _parse_principal_data(self) -> tuple[str, str]:
extras is any extension on the principal data portion of the descriptor
string. For example, "isnnbkgnd" would return ("isn", "nbkgnd").
"""
m = re.match(
r"^(drt|ena|int|isn|spx)(?:(?<=spx)\d+)?([^-_\s]*)$", self.principal_data
)
m = PRINCIPAL_DATA_PATTERN.match(self.principal_data)
if not m:
raise ValueError(
"Invalid principal_data format: "
Expand Down Expand Up @@ -469,6 +473,96 @@ def principal_data_var(self) -> str:
"spx": "ena_spectral_index",
}[self.principal_data[:3]]

@property
def principal_data_extras(self) -> str:
"""
The modifier codes following the principal data stem.

These say which corrections a map was made with, e.g. the "nbs" of
"enanbs". Empty if the principal data is a bare stem, or if it does not
parse as a stem followed by modifiers at all.

Returns
-------
principal_data_extras : str
The modifier codes, run together in the order they appear.
"""
m = PRINCIPAL_DATA_PATTERN.match(self.principal_data)
return m.group(2) if m else ""

@property
def raw(self) -> bool:
"""
Whether the map is of the raw data, made with none of the corrections.

Returns
-------
raw : bool
True if this map asks for no corrections at all.
"""
return "raw" in self.principal_data

@property
def sputter_corrected(self) -> bool:
"""
Whether the map has the counts sputtered in from a heavier species removed.

Returns
-------
sputter_corrected : bool
True if this map is sputter corrected.
"""
if not self.principal_data.startswith("ena") or self.raw:
return False
# The stem is followed by the correction codes, in order: "s" or "ns"
# for the sputter correction, then "bs" or "nbs" for the bootstrap one.
return self.principal_data_extras.startswith("s")

@property
def bootstrap_corrected(self) -> bool:
"""
Whether the map has the intensity bled in from higher ESA levels removed.

Returns
-------
bootstrap_corrected : bool
True if this map is bootstrap corrected.
"""
if not self.principal_data.startswith("ena") or self.raw:
return False
# The bootstrap code follows the sputter one, see sputter_corrected.
return re.match(r"(?:n?s)?bs", self.principal_data_extras) is not None

@property
def cg_corrected(self) -> bool:
"""
Whether the map's intensities were moved into the heliospheric frame.

Returns
-------
cg_corrected : bool
True if this map is Compton-Getting corrected.
"""
return self.frame_descriptor == "hf"

@property
def isn_masked(self) -> bool:
"""
Whether the map has the interstellar neutral band masked out.

Returns
-------
isn_masked : bool
True if this map is ISN masked.
"""
if not self.principal_data.startswith("ena") or self.raw:
return False
# The mask code follows the bootstrap one, see bootstrap_corrected.
return (
re.match(r"(?:n?s)?n?bsmsk", self.principal_data_extras, re.IGNORECASE)
is not None
)

# Methods for parsing and building parts of the map descriptor string
@staticmethod
def get_instrument_descriptor(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
pivot_angle,esa_step,intensity_threshold_fraction,angular_width_deg,outlier_percentile
75,1,0.015,80.0,99.999
75,2,0.015,70.0,99.999
75,3,0.015,70.0,99.999
75,4,0.008,70.0,99.999
75,5,0.02,25.0,96.999
75,6,1.0,35.0,99.999
75,7,1.0,35.0,99.999
90,1,0.01,80.0,99.999
90,2,0.01,70.0,99.999
90,3,0.005,65.0,99.999
90,4,0.005,50.0,99.999
90,5,0.1,45.0,99.999
90,6,1.0,35.0,99.999
90,7,1.0,35.0,99.999
105,1,0.01,80.0,99.999
105,2,0.005,60.0,99.999
105,3,0.001,60.0,99.999
105,4,0.005,40.0,99.999
105,5,0.15,35.0,99.999
105,6,1.0,35.0,99.999
105,7,1.0,35.0,99.999
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
source_species,target_species,target_esa,source_esa,sputter_factor
o,h,1,4,0.236
o,h,2,4,0.372
o,h,3,4,0.898
o,h,4,4,0.891
o,h,5,4,0.037
o,h,5,6,0.32
o,h,6,6,0.32
o,h,7,6,0.22
30 changes: 30 additions & 0 deletions imap_processing/lo/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,36 @@ class LoConstants:
N_SPINS_PER_ESA_LEVEL: int = 4 # Spins per ESA step within one histogram cycle
N_SPIN_ANGLE_BINS: int = 60 # Number of angular bins within a spin

# Bootstrap correction settings. The nominal coefficients of the ancillary
# are scaled by BOOTSTRAP_SCALE before they are applied; the low and high
# scalings bracket that choice and become the systematic error on the
# corrected intensity.
BOOTSTRAP_SCALE: float = 0.5
BOOTSTRAP_SCALE_INTENSITY_HIGH: float = 0.25
BOOTSTRAP_SCALE_INTENSITY_LOW: float = 1.0

# The bootstrap correction of the highest ESA levels needs an ESA level
# above them to subtract. That virtual "ESA 8" channel has no geometric
# factor of its own; its intensity is extrapolated from the top two levels
# with a power law, at this multiple of the top level's center energy.
ESA_8_ENERGY_RATIO: float = 2.1
# Width [pixels] of the neighborhood the spectral index of a pixel that has
# no measurable one is taken from.
BOOTSTRAP_SPECTRAL_INDEX_FILTER_SIZE: int = 3
# The spectral index to extrapolate with when the map has none to offer.
BOOTSTRAP_DEFAULT_SPECTRAL_INDEX: float = 1.6

# Compton-Getting correction settings. The energy [eV] a hydrogen ENA has
# in the spacecraft frame purely from the spacecraft's own motion, i.e.
# 1/2 m_H U^2 at the nominal spacecraft speed of ~30 km/s. The kinematics
# of the correction are scaled by it.
CG_ENA_ENERGY_AT_SPACECRAFT_SPEED_EV: float = 4.661

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the CG correction that I implemented, this was computed exactly using SPICE. Probably is just in the noise.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes - spice is the correct way to go. Almost none of the steps in the dropbox use spice, and I didn't realize you'd already implemented this. I'll look at your implementation and use it instead.

# The predictor-corrector that estimates the source spectrum behind the
# observed one runs until the RMS change in the intensities falls below the
# tolerance, or the iterations run out.
CG_MAX_ITERATIONS: int = 20
CG_CONVERGENCE_TOLERANCE: float = 0.005
Comment on lines +126 to +127

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really big PR, so I didn't track it down, but IIRC, the flux correction is totally separate from the CG correction. Doesn't the flux correction get applied to maps in either the s/c or helio-frame? I would expect these variables to be specifically named to flux correction rather than having to do with the CG correction.


# Nominal spin period [s]. True spin duration is NOT 15 seconds.
NOMINAL_SPIN_PERIOD_SEC: float = 15.0

Expand Down
Loading
Loading