Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed pfss_table separation angles #43

Merged
merged 9 commits into from
Aug 11, 2023
Merged
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
144 changes: 125 additions & 19 deletions solarmach/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@

if coord_sys.lower().startswith('car'):
coord_sys = 'Carrington'
if coord_sys.lower().startswith('sto') or coord_sys.lower() == 'Earth':
if coord_sys.lower().startswith('sto') or coord_sys.lower() == "earth":
coord_sys = 'Stonyhurst'

self.date = date
Expand Down Expand Up @@ -307,12 +307,23 @@
"Latitudinal separation to Earth's latitude": latsep_E_list, 'Vsw': body_vsw_list,
f'Magnetic footpoint longitude ({coord_sys})': footp_long_list})

self.pfss_table = pd.DataFrame(
{"Spacecraft/Body" : list(self.body_dict.keys()),
f"{coord_sys} longitude (°)" : body_lon_list,
f"{coord_sys} latitude (°)" : body_lat_list,
"Heliocentric_distance (R_Sun)" : np.array(body_dist_list) * u.au.to(u.solRad), # Quick conversion of AU -> Solar radii
"Vsw": body_vsw_list
}
)

if self.reference_long is not None:
self.coord_table['Longitudinal separation between body and reference_long'] = longsep_list
self.coord_table[
"Longitudinal separation between body's mangetic footpoint and reference_long"] = footp_longsep_list
self.pfss_table.loc[len(self.pfss_table.index)] = ["Reference Point", self.reference_long, self.reference_lat, 1, np.NaN]
if self.reference_lat is not None:
self.coord_table['Latitudinal separation between body and reference_lat'] = latsep_list
self.pfss_table.loc[self.pfss_table["Spacecraft/Body"]=="Reference Point", f"{coord_sys} latitude (°)"] = self.reference_lat

# Does this still have a use?
pass
Expand Down Expand Up @@ -936,15 +947,23 @@
ax.plot(full_circle_radians, np.ones(len(full_circle_radians))*0.866, c='darkgray', lw=1.5, ls=":", zorder=3) #cos(30deg) = 0.866(O)
ax.plot(full_circle_radians, np.ones(len(full_circle_radians))*0.500, c='darkgray', lw=1.5, ls=":", zorder=3) #cos(60deg) = 0.5(0)

# Plot the gridlines for 10 and 100 solar radii, because this sometimes fails bythe .grid() -method for unkown reason
ax.plot(full_circle_radians, np.ones(len(full_circle_radians))*10, c="gray", lw=0.6, ls='-', zorder=1)
ax.plot(full_circle_radians, np.ones(len(full_circle_radians))*100, c="gray", lw=0.6, ls='-', zorder=1)

Check warning on line 952 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L951-L952

Added lines #L951 - L952 were not covered by tests

# Gather field line objects, photospheric footpoints and magnetic polarities in these lists
# fieldlines is a class attribute, so that the field lines can be neasily 3D plotted with another method
# fieldlines is a class attribute, so that the field lines can be easily 3D plotted with another method
self.fieldlines = []
photospheric_footpoints = []
fieldline_polarities = []

# The radial coordinates for reference parker spiral (plot even outside the figure boundaries to avert visual bugs)
reference_array = np.linspace(rss, r_max+200, int(1e3))

# Longitudinal and latitudinal separation angles to Earth's magnetic footpoint
lon_sep_angles = np.array([])
lat_sep_angles = np.array([])

Check warning on line 965 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L964-L965

Added lines #L964 - L965 were not covered by tests

for i, body_id in enumerate(self.body_dict):

body_lab = self.body_dict[body_id][1]
Expand Down Expand Up @@ -1022,6 +1041,23 @@
photospheric_footpoints.append((fl_lon[0], fl_lat[0]))
fieldline_polarities.append(int(fline_objects[0].polarity))

# Save Earth's magnetic footpoint for later comparison:
if body_lab == "Earth":
earth_footpoint = (fl_lon[0], fl_lat[0])

Check warning on line 1046 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1045-L1046

Added lines #L1045 - L1046 were not covered by tests


# Calculate footpoint separation angles to Earth's footpoint
if "Earth" in self.body_dict:
for footpoint in photospheric_footpoints:
lon_sep = earth_footpoint[0] - footpoint[0]
lon_sep = lon_sep if lon_sep < 180 else lon_sep - 360 # Here check that the separation isn't over half a circle
lat_sep = earth_footpoint[1] - footpoint[1]

Check warning on line 1054 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1050-L1054

Added lines #L1050 - L1054 were not covered by tests

lon_sep_angles = np.append(lon_sep_angles, lon_sep)
lat_sep_angles = np.append(lat_sep_angles, lat_sep)

Check warning on line 1057 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1056-L1057

Added lines #L1056 - L1057 were not covered by tests

self.pfss_table["Footpoint lon separation to Earth's footpoint lon"] = lon_sep_angles
self.pfss_table["Footpoint lat separation to Earth's footpoint lat"] = lat_sep_angles

Check warning on line 1060 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1059-L1060

Added lines #L1059 - L1060 were not covered by tests

# Reference longitude and corresponding parker spiral arm
if self.reference_long:
Expand Down Expand Up @@ -1056,32 +1092,52 @@
self.reference_fieldlines.append(ref_objects[0])

# Also init extreme values for the longitudinal span of the uptracked flux tube
reference_long_min, reference_long_max = 360, 0
self.reference_long_min, self.reference_long_max = 360, 0

Check warning on line 1095 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1095

Added line #L1095 was not covered by tests

# Boolean switch to keep track what kind of arrow/spiral to draw for reference point
open_mag_flux_near_ref_point = False

Check warning on line 1098 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1098

Added line #L1098 was not covered by tests

# Loop the fieldlines, collect them to the list and find the extreme values of longitude at the ss
for ref_vary in varyref_objects:
self.reference_fieldlines.append(ref_vary)

# There still may be closed field lines here, despite trying to avert them in vary_flines() -function. Check here
# that they do not contribute to the max longitude reach at the ss:
if ref_vary.polarity == 0:
continue

Check warning on line 1107 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1106-L1107

Added lines #L1106 - L1107 were not covered by tests
else:
open_mag_flux_near_ref_point = True

Check warning on line 1109 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1109

Added line #L1109 was not covered by tests

# Check the orientation of the field line; is the first index at the photosphere or the last?
idx = 0 if ref_vary.coords.radius.value[0] > ref_vary.coords.radius.value[-1] else -1

# Collect the longitudinal extreme values from the uptracked fluxtube at the source surface height
if ref_vary.coords.lon.value[idx] < reference_long_min:
reference_long_min = ref_vary.coords.lon.value[idx]
if ref_vary.coords.lon.value[idx] > reference_long_max:
reference_long_max = ref_vary.coords.lon.value[idx]
if ref_vary.coords.lon.value[idx] < self.reference_long_min:
self.reference_long_min = ref_vary.coords.lon.value[idx]
if ref_vary.coords.lon.value[idx] > self.reference_long_max:
self.reference_long_max = ref_vary.coords.lon.value[idx]

Check warning on line 1118 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1115-L1118

Added lines #L1115 - L1118 were not covered by tests

arrow_dist = rss-0.80
ref_arr = plt.arrow(np.deg2rad(reference_long_min), 1, 0, arrow_dist, head_width=0.05, head_length=0.2, edgecolor='black',
facecolor='black', lw=0, zorder=7, overhang=0.1)
ref_arr = plt.arrow(np.deg2rad(reference_long_max), 1, 0, arrow_dist, head_width=0.05, head_length=0.2, edgecolor='black',
facecolor='black', lw=0, zorder=7, overhang=0.1)
if open_mag_flux_near_ref_point:
ref_arr = plt.arrow(np.deg2rad(self.reference_long_min), 1, 0, arrow_dist, head_width=0.05, head_length=0.2, edgecolor='black',

Check warning on line 1122 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1121-L1122

Added lines #L1121 - L1122 were not covered by tests
facecolor='black', lw=0, zorder=7, overhang=0.1)
ref_arr = plt.arrow(np.deg2rad(self.reference_long_max), 1, 0, arrow_dist, head_width=0.05, head_length=0.2, edgecolor='black',

Check warning on line 1124 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1124

Added line #L1124 was not covered by tests
facecolor='black', lw=0, zorder=7, overhang=0.1)

reference_legend_label = f"reference long.\nsector:\n({np.round(self.reference_long_min,1)}, {np.round(self.reference_long_max,1)})"

Check warning on line 1127 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1127

Added line #L1127 was not covered by tests

if plot_spirals:
else:
ref_arr = plt.arrow(np.deg2rad(self.reference_long), 1, 0, arrow_dist, head_width=0.1, head_length=0.5, edgecolor='black',

Check warning on line 1130 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1130

Added line #L1130 was not covered by tests
facecolor='black', lw=1., zorder=7, overhang=0.5)

reference_legend_label = f"reference long.\n{self.reference_long}" + r" ^{\circ}"

Check warning on line 1133 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1133

Added line #L1133 was not covered by tests

# These two spirals and the space between them gets drawn only if we plot spirals and open magnetic flux was found near the ref point
if plot_spirals and open_mag_flux_near_ref_point:

Check warning on line 1136 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1136

Added line #L1136 was not covered by tests

# Calculate spirals for the flux tube boundaries
alpha_ref_min = np.deg2rad(reference_long_min) + omega_ref / (1000*reference_vsw / sun_radius) * (rss - reference_array) * np.cos(np.deg2rad(ref_lat))
alpha_ref_max = np.deg2rad(reference_long_max) + omega_ref / (1000*reference_vsw / sun_radius) * (rss - reference_array) * np.cos(np.deg2rad(ref_lat))
alpha_ref_min = np.deg2rad(self.reference_long_min) + omega_ref / (1000*reference_vsw / sun_radius) * (rss - reference_array) * np.cos(np.deg2rad(ref_lat))
alpha_ref_max = np.deg2rad(self.reference_long_max) + omega_ref / (1000*reference_vsw / sun_radius) * (rss - reference_array) * np.cos(np.deg2rad(ref_lat))

Check warning on line 1140 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1139-L1140

Added lines #L1139 - L1140 were not covered by tests

# Plot the spirals
min_edge = plt.polar(alpha_ref_min, reference_array * np.cos(np.deg2rad(ref_lat)), lw=0.7, color="grey", alpha=0.45)[0]
Expand All @@ -1094,6 +1150,13 @@

plt.fill_betweenx(y1, x1, x2, lw=0, color="grey", alpha=0.35)

# Here we plot spirals but open magnetic flux was not found -> draw only one spiral
if plot_spirals:

Check warning on line 1154 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1154

Added line #L1154 was not covered by tests

alpha_ref_single = np.deg2rad(self.reference_long) + omega_ref / (1000*reference_vsw / sun_radius) * (rss - reference_array) * np.cos(np.deg2rad(ref_lat))
ax.plot(alpha_ref_single, reference_array * np.cos(np.deg2rad(ref_lat)), color="grey")

Check warning on line 1157 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1156-L1157

Added lines #L1156 - L1157 were not covered by tests


if long_sector is not None:
if isinstance(long_sector, (list,tuple)) and len(long_sector)==2:

Expand Down Expand Up @@ -1193,7 +1256,7 @@
return mpatches.FancyArrow(0, 0.5 * height, width, 0, length_includes_head=True,
head_width=0.75 * height)

leg2 = ax.legend([ref_arr], ['reference long.'], loc=(1.05, 0.6),
leg2 = ax.legend([ref_arr], [reference_legend_label], loc=(1.05, 0.6),

Check warning on line 1259 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1259

Added line #L1259 was not covered by tests
handler_map={mpatches.FancyArrow: HandlerPatch(patch_func=legend_arrow), },
fontsize=15)
ax.add_artist(leg1)
Expand Down Expand Up @@ -1254,9 +1317,19 @@
cb_ax = fig.axes[-1]
cb_ax.set_ylabel('Heliographic latitude [deg]', fontsize=20)

# Add footpoints and magnetic polarities to coord_table
self.coord_table["PFSS_Footpoint"] = photospheric_footpoints
self.coord_table["Magnetic polarity"] = fieldline_polarities
# Add footpoints, magnetic polarities and the reach of reference_long flux tube to PFSS_table
if self.reference_long:
photospheric_footpoints.append(self.reference_long)
fieldline_polarities.append(ref_objects[0].polarity)
self.pfss_table["Reference flux tube lon range"] = [np.NaN if i<len(self.body_dict) else (self.reference_long_min, self.reference_long_max) for i in range(len(self.body_dict)+1)]

Check warning on line 1324 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1321-L1324

Added lines #L1321 - L1324 were not covered by tests

self.pfss_table["Magnetic footpoint (PFSS)"] = photospheric_footpoints
self.pfss_table["Magnetic polarity"] = fieldline_polarities

Check warning on line 1327 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1326-L1327

Added lines #L1326 - L1327 were not covered by tests


# Update solar wind speed to the reference point
if reference_vsw:
self.pfss_table.loc[self.pfss_table["Spacecraft/Body"]=="Reference_point", "Vsw"] = reference_vsw

Check warning on line 1332 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1331-L1332

Added lines #L1331 - L1332 were not covered by tests

# if using streamlit, send plot to streamlit output, else call plt.show()
if _isstreamlit():
Expand Down Expand Up @@ -1351,6 +1424,39 @@

traces.append(fieldline_trace)

# If there is a reference_longitude that was plotted, add it to the list of names
if self.reference_long:

Check warning on line 1428 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1428

Added line #L1428 was not covered by tests

for i, field_line in enumerate(self.reference_fieldlines):

Check warning on line 1430 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1430

Added line #L1430 was not covered by tests

coords = field_line.coords
coords.representation_type = "cartesian"

Check warning on line 1433 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1432-L1433

Added lines #L1432 - L1433 were not covered by tests

if color_code=="polarity":
color = colors.get(field_line.polarity)
if color_code=='object':
color = "black"

Check warning on line 1438 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1435-L1438

Added lines #L1435 - L1438 were not covered by tests

# New object's lines being plotted
if i==0:
if self.reference_lat is None:
ref_lat = 0

Check warning on line 1443 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1441-L1443

Added lines #L1441 - L1443 were not covered by tests
else:
ref_lat = self.reference_lat
line_label = f"Reference_point: {self.reference_long, ref_lat}"
show_in_legend = True

Check warning on line 1447 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1445-L1447

Added lines #L1445 - L1447 were not covered by tests
else:
show_in_legend = False

Check warning on line 1449 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1449

Added line #L1449 was not covered by tests

fieldline_trace = go.Scatter3d(x=coords.x/R_sun, y=coords.y/R_sun, z=coords.z/R_sun,

Check warning on line 1451 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1451

Added line #L1451 was not covered by tests
mode='lines',
line = Line(color = color, width = 3.5),
name = line_label,
showlegend = show_in_legend
)

traces.append(fieldline_trace)

Check warning on line 1458 in solarmach/__init__.py

View check run for this annotation

Codecov / codecov/patch

solarmach/__init__.py#L1458

Added line #L1458 was not covered by tests

if active_area[0]:

# the flare area is bound by the extreme values of longitude and latitude
Expand Down Expand Up @@ -1386,7 +1492,7 @@
equator_line = go.Scatter3d(x=equator_cartesians[0], y=equator_cartesians[1], z=equator_cartesians[2],
mode='lines',
line=Line(color='black', width=5.5),
name="0 Latitude"
name="Solar equator"
)

traces.append(equator_line)
Expand Down
20 changes: 15 additions & 5 deletions solarmach/pfss_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,17 +636,27 @@ def construct_gongmap_filename(timestr, directory):
Constructs a default filepath
"""

yy = timestr[2:4]
mm = timestr[5:7]
dd = timestr[8:10]
hh = timestr[11:13]
# Check that timestr is of correct format. it should be <YYYY-MM-DD HH:mm:ss>
date_components = timestr.split('-')

yy = date_components[0][2:4]
mm = date_components[1]
dd_time = date_components[2].split(' ')
dd = dd_time[0]
hh = dd_time[1][:2]

if len(mm) < 2:
mm = f"0{mm}"
if len(dd) < 2:
dd = f"0{dd}"


# If directory is None, (equivalent to not directory in logic), then use the current directory as a base
if not directory:
directory = os.getcwd()

carrington_rot = sunpy.coordinates.sun.carrington_rotation_number(t=timestr).astype(int) # dynamic CR from date
filename = f"mrzqs{yy}{mm}{dd}*{hh}*{carrington_rot}*.fits.gz"
filename = f"mrzqs{yy}{mm}{dd}t{hh}*c{carrington_rot}_*.fits.gz"
filepath = f"{directory}{os.sep}{filename}"
filepaths = glob.glob(filepath)

Expand Down