solarwindpy 0.0.1.dev0__py3-none-any.whl → 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of solarwindpy might be problematic. Click here for more details.

Files changed (412) hide show
  1. plans/.velocity/metrics.json +96 -0
  2. plans/0-overview-template.md +268 -0
  3. plans/N-phase-template.md +106 -0
  4. plans/PLAN_AUDIT_SUMMARY.md +173 -0
  5. plans/TEMPLATE-USAGE-GUIDE.md +198 -0
  6. plans/__init__.py +1 -0
  7. plans/abandoned/compaction-agent-system/0-Overview.md +123 -0
  8. plans/abandoned/compaction-agent-system/agents-index-update-plan.md +109 -0
  9. plans/abandoned/compaction-agent-system/compacted_state.md +85 -0
  10. plans/abandoned/compaction-agent-system/implementation-plan.md +107 -0
  11. plans/abandoned/compaction-agent-system/system-validation-report.md +159 -0
  12. plans/abandoned/compaction-agent-system/usage-guide.md +210 -0
  13. plans/abandoned/hook-system-enhancement/0-Overview.md +214 -0
  14. plans/abandoned/hook-system-enhancement/1-Phase1-Core-Infrastructure.md +313 -0
  15. plans/abandoned/hook-system-enhancement/2-Phase2-Intelligent-Testing.md +385 -0
  16. plans/abandoned/hook-system-enhancement/3-Phase3-Physics-Validation.md +444 -0
  17. plans/abandoned/hook-system-enhancement/4-Phase4-Performance-Monitoring.md +458 -0
  18. plans/abandoned/hook-system-enhancement/5-Phase5-Developer-Experience.md +532 -0
  19. plans/abandoned/hook-system-enhancement/6-Implementation-Timeline.md +274 -0
  20. plans/abandoned/hook-system-enhancement/7-Risk-Management.md +376 -0
  21. plans/abandoned/hook-system-enhancement/8-Testing-Strategy.md +579 -0
  22. plans/abandoned/readthedocs-automation/0-Overview.md +247 -0
  23. plans/abandoned/readthedocs-automation/1-Emergency-Documentation-Fixes.md +270 -0
  24. plans/abandoned/readthedocs-automation/2-Template-System-Enhancement.md +811 -0
  25. plans/abandoned/readthedocs-automation/3-Quality-Audit-ReadTheDocs-Integration.md +844 -0
  26. plans/abandoned/readthedocs-automation/4-Plan-Consolidation-Cleanup.md +632 -0
  27. plans/abandoned/readthedocs-automation/9-Closeout.md +207 -0
  28. plans/abandoned/readthedocs-automation/ABANDONMENT_REASON.md +72 -0
  29. plans/cicd-architecture-redesign/0-Overview.md +193 -0
  30. plans/cicd-architecture-redesign/1-Workflow-Creation.md +103 -0
  31. plans/cicd-architecture-redesign/2-Version-Detection.md +123 -0
  32. plans/cicd-architecture-redesign/3-Deployment-Gates.md +169 -0
  33. plans/cicd-architecture-redesign/4-RC-Testing.md +194 -0
  34. plans/cicd-architecture-redesign/5-TestPyPI-Validation.md +264 -0
  35. plans/cicd-architecture-redesign/6-Production-Release.md +263 -0
  36. plans/cicd-architecture-redesign/7-Cleanup.md +243 -0
  37. plans/cicd-architecture-redesign/8-Documentation.md +285 -0
  38. plans/cicd-architecture-redesign/Closeout.md +225 -0
  39. plans/closeout-template.md +259 -0
  40. plans/completed/circular-import-audit/0-Overview.md +152 -0
  41. plans/completed/circular-import-audit/1-Static-Dependency-Analysis.md +62 -0
  42. plans/completed/circular-import-audit/2-Dynamic-Import-Testing.md +56 -0
  43. plans/completed/circular-import-audit/3-Performance-Impact-Assessment.md +56 -0
  44. plans/completed/circular-import-audit/4-Issue-Remediation.md +78 -0
  45. plans/completed/circular-import-audit/5-Preventive-Infrastructure.md +89 -0
  46. plans/completed/claude-settings-ecosystem-alignment/0-Overview.md +162 -0
  47. plans/completed/claude-settings-ecosystem-alignment/1-Security-Foundation.md +148 -0
  48. plans/completed/claude-settings-ecosystem-alignment/2-Hook-Integration.md +158 -0
  49. plans/completed/claude-settings-ecosystem-alignment/3-Agent-System-Integration.md +177 -0
  50. plans/completed/claude-settings-ecosystem-alignment/4-Enhanced-Workflow-Automation.md +159 -0
  51. plans/completed/claude-settings-ecosystem-alignment/5-Validation-Monitoring.md +181 -0
  52. plans/completed/claude-settings-ecosystem-alignment/compacted_session_state.md +290 -0
  53. plans/completed/combined_plan_with_checklist_documentation/1-Overview-and-Goals.md +51 -0
  54. plans/completed/combined_plan_with_checklist_documentation/2-Toolchain-and-Hosting.md +69 -0
  55. plans/completed/combined_plan_with_checklist_documentation/3-Repository-Structure.md +61 -0
  56. plans/completed/combined_plan_with_checklist_documentation/4-Configuration-and-Standards.md +70 -0
  57. plans/completed/combined_plan_with_checklist_documentation/5-Documentation-Content.md +62 -0
  58. plans/completed/combined_plan_with_checklist_documentation/6-CI-CD-and-Validation.md +58 -0
  59. plans/completed/combined_plan_with_checklist_documentation/7-Maintenance.md +55 -0
  60. plans/completed/combined_test_plan_with_checklist_fitfunctions/0-Overview.md +135 -0
  61. plans/completed/combined_test_plan_with_checklist_fitfunctions/1-Common-fixtures.md +59 -0
  62. plans/completed/combined_test_plan_with_checklist_fitfunctions/10-power_laws.md +56 -0
  63. plans/completed/combined_test_plan_with_checklist_fitfunctions/2-core.py-FitFunction.md +118 -0
  64. plans/completed/combined_test_plan_with_checklist_fitfunctions/3-gaussians.py-Gaussian-GaussianNormalized-GaussianLn.md +69 -0
  65. plans/completed/combined_test_plan_with_checklist_fitfunctions/4-trend_fits.py-TrendFit.md +99 -0
  66. plans/completed/combined_test_plan_with_checklist_fitfunctions/5-plots.py-FFPlot.md +98 -0
  67. plans/completed/combined_test_plan_with_checklist_fitfunctions/6-tex_info.py-TeXinfo.md +79 -0
  68. plans/completed/combined_test_plan_with_checklist_fitfunctions/7-Justification.md +49 -0
  69. plans/completed/combined_test_plan_with_checklist_fitfunctions/8-exponentials.md +64 -0
  70. plans/completed/combined_test_plan_with_checklist_fitfunctions/9-lines.md +58 -0
  71. plans/completed/combined_test_plan_with_checklist_plotting/0-Overview.md +142 -0
  72. plans/completed/combined_test_plan_with_checklist_plotting/1-base.py.md +90 -0
  73. plans/completed/combined_test_plan_with_checklist_plotting/10-labels-special.py.md +102 -0
  74. plans/completed/combined_test_plan_with_checklist_plotting/11-labels-chemistry.py.md +212 -0
  75. plans/completed/combined_test_plan_with_checklist_plotting/12-labels-composition.py.md +242 -0
  76. plans/completed/combined_test_plan_with_checklist_plotting/13-labels-datetime.py.md +247 -0
  77. plans/completed/combined_test_plan_with_checklist_plotting/14-labels-elemental_abundance.py.md +274 -0
  78. plans/completed/combined_test_plan_with_checklist_plotting/15-visual-validation.md +256 -0
  79. plans/completed/combined_test_plan_with_checklist_plotting/16-integration-testing.md +266 -0
  80. plans/completed/combined_test_plan_with_checklist_plotting/17-performance-benchmarks.md +267 -0
  81. plans/completed/combined_test_plan_with_checklist_plotting/18-Fixtures-and-Utilities.md +86 -0
  82. plans/completed/combined_test_plan_with_checklist_plotting/2-agg_plot.py.md +90 -0
  83. plans/completed/combined_test_plan_with_checklist_plotting/3-histograms.py.md +201 -0
  84. plans/completed/combined_test_plan_with_checklist_plotting/4-scatter.py.md +167 -0
  85. plans/completed/combined_test_plan_with_checklist_plotting/5-spiral.py.md +216 -0
  86. plans/completed/combined_test_plan_with_checklist_plotting/6-orbits.py.md +108 -0
  87. plans/completed/combined_test_plan_with_checklist_plotting/7-tools.py.md +86 -0
  88. plans/completed/combined_test_plan_with_checklist_plotting/8-select_data_from_figure.py.md +97 -0
  89. plans/completed/combined_test_plan_with_checklist_plotting/9-labels-base.py.md +88 -0
  90. plans/completed/combined_test_plan_with_checklist_solar_activity/.gitkeep +0 -0
  91. plans/completed/combined_test_plan_with_checklist_solar_activity/0-Overview.md +170 -0
  92. plans/completed/combined_test_plan_with_checklist_solar_activity/1-Package-Entry-Point-__init__.py.md +121 -0
  93. plans/completed/combined_test_plan_with_checklist_solar_activity/2-Core-Base-Classes-base.py.md +142 -0
  94. plans/completed/combined_test_plan_with_checklist_solar_activity/3-Plotting-Helpers-plots.py.md +123 -0
  95. plans/completed/combined_test_plan_with_checklist_solar_activity/4-LISIRD-Sub-package.md +119 -0
  96. plans/completed/combined_test_plan_with_checklist_solar_activity/5-Extrema-Calculator.md +103 -0
  97. plans/completed/combined_test_plan_with_checklist_solar_activity/6-Sunspot-Number-Sub-package.md +163 -0
  98. plans/completed/combined_test_plan_with_checklist_solar_activity/7-Sunspot-Number-Init.py.md +217 -0
  99. plans/completed/combined_test_plan_with_checklist_solar_activity/compacted_state.md +52 -0
  100. plans/completed/compaction-agent-modernization/0-Overview.md +156 -0
  101. plans/completed/compaction-agent-modernization/1-Architecture-Audit-Gap-Analysis.md +132 -0
  102. plans/completed/compaction-agent-modernization/2-Token-Baseline-Recalibration.md +153 -0
  103. plans/completed/compaction-agent-modernization/3-Agent-Reference-Updates.md +184 -0
  104. plans/completed/compaction-agent-modernization/4-Compression-Algorithm-Modernization.md +238 -0
  105. plans/completed/compaction-agent-modernization/5-Workflow-Integration-Streamlining.md +252 -0
  106. plans/completed/compaction-agent-modernization/6-Template-Structure-Optimization.md +240 -0
  107. plans/completed/compaction-agent-modernization/7-Integration-Testing-Validation.md +292 -0
  108. plans/completed/compaction-hook-enhancement/0-Overview.md +150 -0
  109. plans/completed/compaction-hook-enhancement/1-Token-Estimation-Enhancement.md +179 -0
  110. plans/completed/compaction-hook-enhancement/2-Compression-Intelligence.md +294 -0
  111. plans/completed/compaction-hook-enhancement/3-Git-Integration-Metadata.md +310 -0
  112. plans/completed/compaction-hook-enhancement/4-Session-Continuity-Features.md +358 -0
  113. plans/completed/compaction-hook-enhancement/5-Testing-Strategy.md +404 -0
  114. plans/completed/compaction-hook-enhancement/6-Integration-Roadmap.md +319 -0
  115. plans/completed/compaction-hook-enhancement/compacted_state.md +142 -0
  116. plans/completed/docstring-audit-enhancement/0-Overview.md +274 -0
  117. plans/completed/docstring-audit-enhancement/1-Infrastructure-Setup-and-Validation-Tools.md +206 -0
  118. plans/completed/docstring-audit-enhancement/2-Core-Physics-Modules-Enhancement.md +237 -0
  119. plans/completed/docstring-audit-enhancement/3-Fitfunctions-Mathematical-Modules-Enhancement.md +188 -0
  120. plans/completed/docstring-audit-enhancement/4-Plotting-Visualization-Modules-Enhancement.md +243 -0
  121. plans/completed/docstring-audit-enhancement/5-Specialized-Modules-Enhancement.md +216 -0
  122. plans/completed/docstring-audit-enhancement/6-Validation-and-Integration.md +216 -0
  123. plans/completed/fitfunctions-testing-implementation/0-Overview.md +130 -0
  124. plans/completed/fitfunctions-testing-implementation/1-Test-Infrastructure-Setup.md +79 -0
  125. plans/completed/fitfunctions-testing-implementation/2-Common-Fixtures-Test-Utilities.md +104 -0
  126. plans/completed/fitfunctions-testing-implementation/3-Core-FitFunction-Testing.md +168 -0
  127. plans/completed/fitfunctions-testing-implementation/4-Specialized-Function-Classes.md +210 -0
  128. plans/completed/fitfunctions-testing-implementation/5-Advanced-Classes-Testing.md +214 -0
  129. plans/completed/fitfunctions-testing-implementation/6-Plotting-Integration-Testing.md +231 -0
  130. plans/completed/fitfunctions-testing-implementation/7-Extended-Coverage-BONUS.md +184 -0
  131. plans/completed/numpy-docstring-conversion-plan/numpy-docstring-conversion-plan.md +118 -0
  132. plans/completed/pr-review-remediation/0-Overview.md +138 -0
  133. plans/completed/pr-review-remediation/1-Critical-Safety-Improvements.md +179 -0
  134. plans/completed/pr-review-remediation/2-Smart-Timeouts-Validation.md +399 -0
  135. plans/completed/pr-review-remediation/3-Enhanced-GitHub-Integration.md +258 -0
  136. plans/completed/pr-review-remediation/compacted_state.md +66 -0
  137. plans/completed/python-310-migration/0-Overview.md +390 -0
  138. plans/completed/python-310-migration/1-Planning-Setup.md +164 -0
  139. plans/completed/python-310-migration/2-Implementation.md +256 -0
  140. plans/completed/python-310-migration/3-Testing-Validation.md +335 -0
  141. plans/completed/python-310-migration/4-Documentation-Release.md +274 -0
  142. plans/completed/python-310-migration/5-Closeout.md +252 -0
  143. plans/completed/requirements-management-consolidation/0-Overview.md +118 -0
  144. plans/completed/requirements-management-consolidation/1-Documentation-Validation-Environment-Setup.md +116 -0
  145. plans/completed/requirements-management-consolidation/2-Requirements-Consolidation.md +161 -0
  146. plans/completed/requirements-management-consolidation/3-Workflow-Automation-Final-Integration.md +196 -0
  147. plans/completed/single-ecosystem-plan-implementation/0-Overview.md +83 -0
  148. plans/completed/single-ecosystem-plan-implementation/1-Plan-Preservation-Session-Management.md +38 -0
  149. plans/completed/single-ecosystem-plan-implementation/2-File-Structure-Optimization.md +43 -0
  150. plans/completed/single-ecosystem-plan-implementation/3-Plan-Migration-Archive-Setup.md +82 -0
  151. plans/completed/single-ecosystem-plan-implementation/4-Agent-System-Transformation.md +108 -0
  152. plans/completed/single-ecosystem-plan-implementation/5-Template-System-Enhancement.md +131 -0
  153. plans/completed/single-ecosystem-plan-implementation/6-Final-Validation-Testing.md +120 -0
  154. plans/completed/test-directory-consolidation/0-Overview.md +51 -0
  155. plans/completed/test-directory-consolidation/1-Structure-Preparation.md +82 -0
  156. plans/completed/test-directory-consolidation/2-File-Migration.md +100 -0
  157. plans/completed/test-directory-consolidation/3-Import-Transformation.md +117 -0
  158. plans/completed/test-directory-consolidation/4-Configuration-Consolidation.md +140 -0
  159. plans/completed/test-directory-consolidation/5-Validation.md +152 -0
  160. plans/completed/test-directory-consolidation/6-Cleanup.md +156 -0
  161. plans/completed/test-planning-agents-architecture/0-Overview.md +79 -0
  162. plans/completed/test-planning-agents-architecture/1-Branch-Isolation-Testing.md +49 -0
  163. plans/completed/test-planning-agents-architecture/2-Cross-Branch-Coordination.md +51 -0
  164. plans/completed/test-planning-agents-architecture/3-Merge-Workflow-Testing.md +48 -0
  165. plans/deployment-semver-pypi-rtd/0-Overview.md +463 -0
  166. plans/deployment-semver-pypi-rtd/1-Semantic-Versioning-Foundation.md +136 -0
  167. plans/deployment-semver-pypi-rtd/2-PyPI-Deployment-Infrastructure.md +168 -0
  168. plans/deployment-semver-pypi-rtd/3-Release-Automation.md +214 -0
  169. plans/deployment-semver-pypi-rtd/4-Plan-Closeout.md +543 -0
  170. plans/deployment-semver-pypi-rtd/compacted_session_state.md +172 -0
  171. plans/deployment-semver-pypi-rtd/compacted_state.md +131 -0
  172. plans/documentation-code-audit/0-Overview.md +393 -0
  173. plans/documentation-code-audit/1-Discovery-Inventory.md +183 -0
  174. plans/documentation-code-audit/2-Execution-Environment-Setup.md +263 -0
  175. plans/documentation-code-audit/3-Systematic-Validation.md +322 -0
  176. plans/documentation-code-audit/4-Code-Example-Remediation.md +358 -0
  177. plans/documentation-code-audit/5-Physics-MultiIndex-Compliance.md +464 -0
  178. plans/documentation-code-audit/6-Doctest-Integration.md +523 -0
  179. plans/documentation-code-audit/7-Reporting-Documentation.md +498 -0
  180. plans/documentation-code-audit/8-Closeout.md +456 -0
  181. plans/documentation-rebuild-session/compacted_state.md +109 -0
  182. plans/documentation-rendering-fixes/0-Overview.md +104 -0
  183. plans/documentation-rendering-fixes/1-Sphinx-Build-Diagnostics-Warning-Audit.md +101 -0
  184. plans/documentation-rendering-fixes/2-Configuration-Infrastructure-Fixes.md +113 -0
  185. plans/documentation-rendering-fixes/3-Docstring-Syntax-Audit-Repair.md +131 -0
  186. plans/documentation-rendering-fixes/4-HTML-Page-Rendering-Verification.md +113 -0
  187. plans/documentation-rendering-fixes/5-Advanced-Documentation-Quality-Assurance.md +119 -0
  188. plans/documentation-rendering-fixes/6-Documentation-Build-Optimization-Testing.md +129 -0
  189. plans/documentation-rendering-fixes/compacted_state.md +132 -0
  190. plans/documentation-template-fix/0-Overview.md +197 -0
  191. plans/documentation-template-fix/1-Template-System-Analysis.md +269 -0
  192. plans/documentation-template-fix/2-Template-Modification.md +609 -0
  193. plans/documentation-template-fix/3-Build-System-Integration.md +766 -0
  194. plans/documentation-template-fix/4-Testing-Validation.md +1399 -0
  195. plans/documentation-template-fix/5-Documentation-Training.md +602 -0
  196. plans/documentation-workflow-fix/0-Overview.md +222 -0
  197. plans/documentation-workflow-fix/1-Immediate-Fixes.md +238 -0
  198. plans/documentation-workflow-fix/2-Configuration-Setup.md +298 -0
  199. plans/documentation-workflow-fix/3-Pre-commit-Integration.md +382 -0
  200. plans/documentation-workflow-fix/4-Workflow-Improvements.md +446 -0
  201. plans/documentation-workflow-fix/5-Documentation-and-Training.md +527 -0
  202. plans/duplicate-object-warnings-fix-plan.md +130 -0
  203. plans/github-issues-migration/0-Overview.md +510 -0
  204. plans/github-issues-migration/1-Foundation-Label-System.md +180 -0
  205. plans/github-issues-migration/2-Migration-Tool-Rewrite.md +235 -0
  206. plans/github-issues-migration/3-CLI-Integration-Automation.md +169 -0
  207. plans/github-issues-migration/4-Validated-Migration.md +252 -0
  208. plans/github-issues-migration/5-Documentation-Training.md +171 -0
  209. plans/github-issues-migration/6-Closeout.md +179 -0
  210. plans/github-workflows-repair/repair-plan.md +299 -0
  211. plans/issues_from_plans.py +342 -0
  212. plans/pr-270-doc-validation-fixes/0-Overview.md +354 -0
  213. plans/pr-270-doc-validation-fixes/1-Critical-PR-Fixes.md +117 -0
  214. plans/pr-270-doc-validation-fixes/2-Framework-Right-Sizing.md +129 -0
  215. plans/pr-270-doc-validation-fixes/3-Sustainable-Documentation.md +126 -0
  216. plans/pr-270-doc-validation-fixes/4-Closeout-Migration.md +143 -0
  217. plans/pr-270-doc-validation-fixes/PLAN_COMPLETED.md +149 -0
  218. plans/python-310-migration/0-Overview.md +390 -0
  219. plans/python-310-migration/1-Planning-Setup.md +164 -0
  220. plans/python-310-migration/2-Implementation.md +256 -0
  221. plans/python-310-migration/3-Testing-Validation.md +335 -0
  222. plans/python-310-migration/4-Documentation-Release.md +274 -0
  223. plans/python-310-migration/5-Closeout.md +252 -0
  224. plans/readthedocs-simplified/0-Overview.md +243 -0
  225. plans/readthedocs-simplified/1-Immediate-Fixes.md +216 -0
  226. plans/readthedocs-simplified/2-Template-Simplification.md +278 -0
  227. plans/readthedocs-simplified/3-ReadTheDocs-Setup.md +298 -0
  228. plans/readthedocs-simplified/4-Testing-Validation.md +328 -0
  229. plans/readthedocs-simplified/5-Closeout.md +231 -0
  230. plans/readthedocs-simplified/compacted_state.md +127 -0
  231. plans/session-compaction-2025-08-12/compacted_state.md +114 -0
  232. plans/session-compaction-2025-08-13/compacted_state.md +145 -0
  233. plans/session-continuity-protocol/0-Overview.md +35 -0
  234. plans/session-continuity-protocol/1-Core-Principles-Framework.md +40 -0
  235. plans/session-continuity-protocol/2-Pre-Session-Validation-System.md +79 -0
  236. plans/session-continuity-protocol/3-Context-Switching-Prevention.md +87 -0
  237. plans/session-continuity-protocol/4-Progress-Tracking-Recovery.md +100 -0
  238. plans/sphinx-warnings-analysis.md +222 -0
  239. plans/systemprompt-optimization/0-Overview.md +447 -0
  240. plans/systemprompt-optimization/1-Deploy-SystemPrompt.md +114 -0
  241. plans/systemprompt-optimization/2-Documentation-Alignment.md +198 -0
  242. plans/systemprompt-optimization/3-Monitoring-Infrastructure.md +396 -0
  243. plans/systemprompt-optimization/4-Implementation-Script.md +450 -0
  244. plans/systemprompt-optimization/9-Closeout.md +165 -0
  245. plans/systemprompt-optimization/compacted_state.md +143 -0
  246. plans/template-value-propositions/0-Overview.md +357 -0
  247. plans/template-value-propositions/1-Value-Proposition-Framework-Design.md +144 -0
  248. plans/template-value-propositions/2-Plan-Template-Enhancement.md +178 -0
  249. plans/template-value-propositions/3-Value-Generator-Hook-Implementation.md +291 -0
  250. plans/template-value-propositions/4-Value-Validator-Hook-Implementation.md +274 -0
  251. plans/template-value-propositions/5-Documentation-Agent-Updates.md +219 -0
  252. plans/template-value-propositions/6-Integration-Testing-Validation.md +247 -0
  253. plans/tests-audit/0-Overview.md +410 -0
  254. plans/tests-audit/1-Discovery-Inventory.md +170 -0
  255. plans/tests-audit/2-Physics-Validation-Audit.md +195 -0
  256. plans/tests-audit/3-Architecture-Compliance.md +195 -0
  257. plans/tests-audit/4-Numerical-Stability-Analysis.md +203 -0
  258. plans/tests-audit/5-Documentation-Enhancement.md +220 -0
  259. plans/tests-audit/6-Audit-Deliverables.md +220 -0
  260. plans/tests-audit/7-Closeout.md +252 -0
  261. plans/tests-audit/artifacts/ARCHITECTURE_COMPLIANCE_REPORT.md +315 -0
  262. plans/tests-audit/artifacts/ARCHITECTURE_RECOMMENDATIONS.md +943 -0
  263. plans/tests-audit/artifacts/COMPREHENSIVE_AUDIT_REPORT.md +356 -0
  264. plans/tests-audit/artifacts/CONTRIBUTING_ENHANCED_TEMPLATE.md +419 -0
  265. plans/tests-audit/artifacts/COVERAGE_GAP_ANALYSIS.md +152 -0
  266. plans/tests-audit/artifacts/DOCUMENTATION_ENHANCEMENT_REPORT.md +502 -0
  267. plans/tests-audit/artifacts/EXECUTIVE_AUDIT_SUMMARY.md +129 -0
  268. plans/tests-audit/artifacts/IMPLEMENTATION_ROADMAP.md +647 -0
  269. plans/tests-audit/artifacts/NUMERICAL_RECOMMENDATIONS.md +739 -0
  270. plans/tests-audit/artifacts/NUMERICAL_STABILITY_GUIDE_TEMPLATE.rst +451 -0
  271. plans/tests-audit/artifacts/NUMERICAL_STABILITY_REPORT.md +301 -0
  272. plans/tests-audit/artifacts/PHASE_3_SUMMARY.md +280 -0
  273. plans/tests-audit/artifacts/PHASE_4_SUMMARY.md +229 -0
  274. plans/tests-audit/artifacts/PHASE_5_SUMMARY.md +292 -0
  275. plans/tests-audit/artifacts/PHASE_6_CLOSEOUT.md +278 -0
  276. plans/tests-audit/artifacts/PHYSICS_GUIDE_TEMPLATE.rst +268 -0
  277. plans/tests-audit/artifacts/PHYSICS_VALIDATION_REPORT.md +235 -0
  278. plans/tests-audit/artifacts/TECHNICAL_DELIVERABLES_PACKAGE.md +2502 -0
  279. plans/tests-audit/artifacts/TEST_INVENTORY.csv +1204 -0
  280. plans/tests-audit/artifacts/TEST_INVENTORY.md +135 -0
  281. plans/tests-audit/artifacts/test_discovery_analysis.py +231 -0
  282. plans/tests-audit/artifacts/test_parser.py +395 -0
  283. solarwindpy/README.md +3 -0
  284. solarwindpy/Untitled.ipynb +54 -0
  285. solarwindpy/__init__.py +74 -0
  286. solarwindpy/core/__init__.py +23 -0
  287. solarwindpy/core/alfvenic_turbulence.py +804 -0
  288. solarwindpy/core/base.py +267 -0
  289. solarwindpy/core/ions.py +309 -0
  290. solarwindpy/core/plasma.py +2133 -0
  291. solarwindpy/core/spacecraft.py +256 -0
  292. solarwindpy/core/tensor.py +90 -0
  293. solarwindpy/core/units_constants.py +199 -0
  294. solarwindpy/core/vector.py +328 -0
  295. solarwindpy/fitfunctions/__init__.py +20 -0
  296. solarwindpy/fitfunctions/core.py +734 -0
  297. solarwindpy/fitfunctions/exponentials.py +188 -0
  298. solarwindpy/fitfunctions/gaussians.py +264 -0
  299. solarwindpy/fitfunctions/lines.py +116 -0
  300. solarwindpy/fitfunctions/moyal.py +71 -0
  301. solarwindpy/fitfunctions/plots.py +751 -0
  302. solarwindpy/fitfunctions/power_laws.py +209 -0
  303. solarwindpy/fitfunctions/tex_info.py +568 -0
  304. solarwindpy/fitfunctions/trend_fits.py +482 -0
  305. solarwindpy/instabilities/__init__.py +16 -0
  306. solarwindpy/instabilities/beta_ani.py +82 -0
  307. solarwindpy/instabilities/verscharen2016.py +631 -0
  308. solarwindpy/plotting/__init__.py +33 -0
  309. solarwindpy/plotting/agg_plot.py +489 -0
  310. solarwindpy/plotting/base.py +465 -0
  311. solarwindpy/plotting/hist1d.py +405 -0
  312. solarwindpy/plotting/hist2d.py +1035 -0
  313. solarwindpy/plotting/histograms.py +1845 -0
  314. solarwindpy/plotting/labels/__init__.py +104 -0
  315. solarwindpy/plotting/labels/base.py +686 -0
  316. solarwindpy/plotting/labels/chemistry.py +19 -0
  317. solarwindpy/plotting/labels/composition.py +100 -0
  318. solarwindpy/plotting/labels/datetime.py +235 -0
  319. solarwindpy/plotting/labels/elemental_abundance.py +73 -0
  320. solarwindpy/plotting/labels/special.py +794 -0
  321. solarwindpy/plotting/orbits.py +515 -0
  322. solarwindpy/plotting/scatter.py +99 -0
  323. solarwindpy/plotting/select_data_from_figure.py +329 -0
  324. solarwindpy/plotting/spiral.py +980 -0
  325. solarwindpy/plotting/tools.py +434 -0
  326. solarwindpy/scripts/__init__.py +1 -0
  327. solarwindpy/scripts/logs/.gitignore +1 -0
  328. solarwindpy/solar_activity/__init__.py +53 -0
  329. solarwindpy/solar_activity/base.py +605 -0
  330. solarwindpy/solar_activity/lisird/__init__.py +3 -0
  331. solarwindpy/solar_activity/lisird/extrema_calculator.py +394 -0
  332. solarwindpy/solar_activity/lisird/lisird.py +319 -0
  333. solarwindpy/solar_activity/plots.py +116 -0
  334. solarwindpy/solar_activity/sunspot_number/.DS_Store +0 -0
  335. solarwindpy/solar_activity/sunspot_number/__init__.py +3 -0
  336. solarwindpy/solar_activity/sunspot_number/sidc.py +556 -0
  337. solarwindpy/solar_activity/sunspot_number/ssn_extrema.csv +72 -0
  338. solarwindpy/solar_activity/sunspot_number/ssn_extrema.csv.silso +72 -0
  339. solarwindpy/tools/__init__.py +162 -0
  340. solarwindpy-0.1.1.dist-info/METADATA +181 -0
  341. solarwindpy-0.1.1.dist-info/RECORD +409 -0
  342. {solarwindpy-0.0.1.dev0.dist-info → solarwindpy-0.1.1.dist-info}/WHEEL +1 -1
  343. solarwindpy-0.1.1.dist-info/licenses/LICENSE.rst +32 -0
  344. solarwindpy-0.1.1.dist-info/top_level.txt +3 -0
  345. tests/__init__.py +1 -0
  346. tests/conftest.py +10 -0
  347. tests/core/__init__.py +1 -0
  348. tests/core/test_alfvenic_turbulence.py +544 -0
  349. tests/core/test_base.py +112 -0
  350. tests/core/test_base_head_tail.py +29 -0
  351. tests/core/test_base_mi_tuples.py +11 -0
  352. tests/core/test_core_verify_datetimeindex.py +32 -0
  353. tests/core/test_ions.py +325 -0
  354. tests/core/test_plasma.py +2581 -0
  355. tests/core/test_plasma_io.py +12 -0
  356. tests/core/test_quantities.py +507 -0
  357. tests/core/test_spacecraft.py +210 -0
  358. tests/core/test_units_constants.py +22 -0
  359. tests/data/epoch.csv +4 -0
  360. tests/data/plasma.csv +4 -0
  361. tests/data/spacecraft.csv +4 -0
  362. tests/fitfunctions/conftest.py +60 -0
  363. tests/fitfunctions/test_core.py +193 -0
  364. tests/fitfunctions/test_exponentials.py +342 -0
  365. tests/fitfunctions/test_gaussians.py +142 -0
  366. tests/fitfunctions/test_lines.py +349 -0
  367. tests/fitfunctions/test_moyal.py +258 -0
  368. tests/fitfunctions/test_plots.py +258 -0
  369. tests/fitfunctions/test_power_laws.py +365 -0
  370. tests/fitfunctions/test_tex_info.py +183 -0
  371. tests/fitfunctions/test_trend_fit_properties.py +31 -0
  372. tests/fitfunctions/test_trend_fits.py +244 -0
  373. tests/plotting/__init__.py +1 -0
  374. tests/plotting/labels/__init__.py +1 -0
  375. tests/plotting/labels/test_chemistry.py +243 -0
  376. tests/plotting/labels/test_composition.py +345 -0
  377. tests/plotting/labels/test_datetime.py +445 -0
  378. tests/plotting/labels/test_elemental_abundance.py +366 -0
  379. tests/plotting/labels/test_init.py +66 -0
  380. tests/plotting/labels/test_labels_base.py +347 -0
  381. tests/plotting/labels/test_special.py +550 -0
  382. tests/plotting/test_agg_plot.py +602 -0
  383. tests/plotting/test_base.py +752 -0
  384. tests/plotting/test_fixtures_utilities.py +775 -0
  385. tests/plotting/test_histograms.py +546 -0
  386. tests/plotting/test_integration.py +675 -0
  387. tests/plotting/test_orbits.py +435 -0
  388. tests/plotting/test_performance.py +708 -0
  389. tests/plotting/test_scatter.py +752 -0
  390. tests/plotting/test_select_data_from_figure.py +1209 -0
  391. tests/plotting/test_spiral.py +573 -0
  392. tests/plotting/test_tools.py +607 -0
  393. tests/plotting/test_visual_validation.py +465 -0
  394. tests/solar_activity/__init__.py +1 -0
  395. tests/solar_activity/lisird/__init__.py +1 -0
  396. tests/solar_activity/lisird/test_extrema_calculator.py +593 -0
  397. tests/solar_activity/lisird/test_lisird_id.py +187 -0
  398. tests/solar_activity/sunspot_number/__init__.py +1 -0
  399. tests/solar_activity/sunspot_number/test_init.py +399 -0
  400. tests/solar_activity/sunspot_number/test_sidc.py +465 -0
  401. tests/solar_activity/sunspot_number/test_sidc_id.py +223 -0
  402. tests/solar_activity/sunspot_number/test_sidc_loader.py +275 -0
  403. tests/solar_activity/sunspot_number/test_ssn_extrema.py +406 -0
  404. tests/solar_activity/test_base.py +656 -0
  405. tests/solar_activity/test_init.py +396 -0
  406. tests/solar_activity/test_plots.py +371 -0
  407. tests/test_circular_imports.py +408 -0
  408. tests/test_issue_titles.py +25 -0
  409. tests/test_statusline.py +298 -0
  410. solarwindpy-0.0.1.dev0.dist-info/METADATA +0 -14
  411. solarwindpy-0.0.1.dev0.dist-info/RECORD +0 -4
  412. solarwindpy-0.0.1.dev0.dist-info/top_level.txt +0 -1
@@ -0,0 +1,980 @@
1
+ #!/usr/bin/env python
2
+ r"""Spiral mesh plots and associated binning utilities."""
3
+
4
+ import pdb # noqa: F401
5
+ import logging
6
+
7
+ import numpy as np
8
+ import pandas as pd
9
+ import matplotlib as mpl
10
+
11
+ from datetime import datetime
12
+ from numbers import Number
13
+ from collections import namedtuple
14
+ from numba import njit, prange
15
+
16
+ from matplotlib import pyplot as plt
17
+
18
+ from . import base
19
+ from . import labels as labels_module
20
+
21
+ InitialSpiralEdges = namedtuple("InitialSpiralEdges", "x,y")
22
+ # SpiralMeshData = namedtuple("SpiralMeshData", "x,y")
23
+ SpiralMeshBinID = namedtuple("SpiralMeshBinID", "id,fill,visited")
24
+ SpiralFilterThresholds = namedtuple(
25
+ "SpiralFilterThresholds", "density,size", defaults=(False,)
26
+ )
27
+
28
+
29
+ @njit(parallel=True)
30
+ def get_counts_per_bin(bins, x, y):
31
+ nbins = bins.shape[0]
32
+ cell_count = np.full(nbins, 0, dtype=np.int64)
33
+
34
+ for i in prange(nbins):
35
+ x0, x1, y0, y1 = bins[i]
36
+ left = x >= x0
37
+ right = x < x1
38
+ bottom = y >= y0
39
+ top = y < y1
40
+ chk_cell = left & right & bottom & top
41
+ cell_count[i] = chk_cell.sum()
42
+
43
+ return cell_count
44
+
45
+
46
+ @njit(parallel=True)
47
+ def calculate_bin_number_with_numba(mesh, x, y):
48
+ fill = -9999
49
+ zbin = np.full(x.size, fill, dtype=np.int64)
50
+
51
+ nbins = mesh.shape[0]
52
+ bin_visited = np.zeros(nbins, dtype=np.int64)
53
+ for i in prange(nbins):
54
+ x0, x1, y0, y1 = mesh[i]
55
+
56
+ # Assume that largest x- and y-edges are extended by larger of 1% and 0.01
57
+ # so that we can just naively use < instead of a special case of <=.
58
+ # At time of writing (20200418), `SpiralPlot.initialize_mesh` did this.
59
+ tk = (x >= x0) & (x < x1) & (y >= y0) & (y < y1)
60
+
61
+ zbin[tk] = i
62
+ bin_visited[i] += 1
63
+
64
+ return zbin, fill, bin_visited
65
+
66
+
67
+ class SpiralMesh(object):
68
+ def __init__(self, x, y, initial_xedges, initial_yedges, min_per_bin=250):
69
+ self.set_data(x, y)
70
+ self.set_min_per_bin(min_per_bin)
71
+ self.set_initial_edges(initial_xedges, initial_yedges)
72
+ self._cell_filter_thresholds = SpiralFilterThresholds(density=False, size=False)
73
+
74
+ @property
75
+ def bin_id(self):
76
+ return self._bin_id
77
+
78
+ @property
79
+ def cat(self):
80
+ r""":py:class:`pd.Categorical` version of `bin_id`, with fill bin removed."""
81
+ return self._cat
82
+
83
+ @property
84
+ def data(self):
85
+ return self._data
86
+
87
+ @property
88
+ def initial_edges(self):
89
+ return self._initial_edges
90
+
91
+ @property
92
+ def mesh(self):
93
+ return self._mesh
94
+
95
+ @property
96
+ def min_per_bin(self):
97
+ return self._min_per_bin
98
+
99
+ @property
100
+ def cell_filter_thresholds(self):
101
+ return self._cell_filter_thresholds
102
+
103
+ @property
104
+ def cell_filter(self):
105
+ r"""Boolean :py:class:`Series` identifying properly filled mesh cells.
106
+
107
+ Series selects mesh cells that meet density and area criteria specified
108
+ by :py:meth:`mesh_cell_filter_thresholds`.
109
+
110
+ Notes
111
+ ----
112
+ Neither `density` nor `size` convert log-scale edges into linear scale.
113
+ Doing so would overweight the area of mesh cells at larger values on a
114
+ given axis.
115
+ """
116
+ density = self.cell_filter_thresholds.density
117
+ size = self.cell_filter_thresholds.size
118
+
119
+ x = self.mesh[:, [0, 1]]
120
+ y = self.mesh[:, [2, 3]]
121
+
122
+ dx = x[:, 1] - x[:, 0]
123
+ dy = y[:, 1] - y[:, 0]
124
+ dA = dx * dy
125
+
126
+ tk = np.full_like(dx, True, dtype=bool)
127
+ if size:
128
+ size_quantile = np.quantile(dA, size)
129
+ tk_size = dA < size_quantile
130
+ tk = tk & (tk_size)
131
+ if density:
132
+ cnt = np.bincount(self.bin_id.id, minlength=self.mesh.shape[0])
133
+ assert cnt.shape == tk.shape
134
+ cell_density = cnt / dA
135
+ density_quantile = np.quantile(cell_density, density)
136
+ tk_density = cell_density > density_quantile
137
+ tk = tk & tk_density
138
+
139
+ return tk
140
+
141
+ def set_cell_filter_thresholds(self, **kwargs):
142
+ r"""Set or update the :py:meth:`mesh_cell_filter_thresholds`.
143
+
144
+ Parameters
145
+ ----------
146
+ density: scalar
147
+ The density quantile above which we want to select bins, e.g.
148
+ above the 0.01 quantile. This ensures that each bin meets some
149
+ sufficient fill factor.
150
+ size: scalar
151
+ The size quantile below which we want to select bins, e.g.
152
+ below the 0.99 quantile. This ensures that the bin isn't so large
153
+ that it will appear as an outlier.
154
+ """
155
+ density = kwargs.pop("density", False)
156
+ size = kwargs.pop("size", False)
157
+ if len(kwargs.keys()):
158
+ extra = "\n".join(["{}: {}".format(k, v) for k, v in kwargs.items()])
159
+ raise KeyError("Unexpected kwarg\n{}".format(extra))
160
+
161
+ self._cell_filter_thresholds = SpiralFilterThresholds(
162
+ density=density, size=size
163
+ )
164
+
165
+ def set_initial_edges(self, xedges, yedges):
166
+ self._initial_edges = InitialSpiralEdges(xedges, yedges)
167
+
168
+ def set_data(self, x, y):
169
+ data = pd.concat({"x": x, "y": y}, axis=1)
170
+ self._data = data # SpiralMeshData(x, y)
171
+
172
+ def set_min_per_bin(self, new):
173
+ self._min_per_bin = int(new)
174
+
175
+ def initialize_bins(self):
176
+ # Leaves initial edges altered when we change maximum edge.
177
+ xbins = self.initial_edges.x
178
+ ybins = self.initial_edges.y
179
+
180
+ # # Account for highest bin = 0 already done in `SpiralPlot2D.initialize_mesh`.
181
+ # xbins[-1] = np.max([0.01, 1.01 * xbins[-1]])
182
+ # ybins[-1] = np.max([0.01, 1.01 * ybins[-1]])
183
+
184
+ left = xbins[:-1]
185
+ right = xbins[1:]
186
+ bottom = ybins[:-1]
187
+ top = ybins[1:]
188
+
189
+ nx = left.size
190
+ ny = bottom.size
191
+
192
+ mesh = np.full((nx * ny, 4), np.nan, dtype=np.float64)
193
+ for x0, x1, i in zip(left, right, range(nx)):
194
+ for y0, y1, j in zip(bottom, top, range(ny)):
195
+ # NOTE: i*ny+j means go to i'th row, which has
196
+ # nrow * number of bins passed. Then go
197
+ # to j'th bin because we have to traverse
198
+ # to the j'th y-bin too.
199
+ mesh[(i * ny) + j] = [x0, x1, y0, y1]
200
+
201
+ mesh = np.array(mesh)
202
+
203
+ # pdb.set_trace()
204
+
205
+ self.initial_mesh = np.array(mesh)
206
+ return mesh
207
+
208
+ @staticmethod
209
+ def process_one_spiral_step(bins, x, y, min_per_bin):
210
+ # print("Processing spiral step", flush=True)
211
+ # start0 = datetime.now()
212
+ cell_count = get_counts_per_bin(bins, x, y)
213
+
214
+ bins_to_replace = cell_count > min_per_bin
215
+ nbins_to_replace = bins_to_replace.sum()
216
+
217
+ if not nbins_to_replace:
218
+ return None, 0
219
+
220
+ xhyh = 0.5 * (bins[:, [0, 2]] + bins[:, [1, 3]])
221
+
222
+ def split_this_cell(idx):
223
+ x0, x1, y0, y1 = bins[idx]
224
+ xh, yh = xhyh[idx]
225
+
226
+ # Reduce calls to `np.array`.
227
+ # Just return a list here.
228
+ split_cell = [
229
+ [x0, xh, y0, yh],
230
+ [xh, x1, y0, yh],
231
+ [xh, x1, yh, y1],
232
+ [x0, xh, yh, y1],
233
+ ]
234
+
235
+ return split_cell
236
+
237
+ new_cells = bins_to_replace.sum() * [None]
238
+ for i, idx in enumerate(np.where(bins_to_replace)[0]):
239
+ new_cells[i] = split_this_cell(idx)
240
+
241
+ new_cells = np.vstack(new_cells)
242
+
243
+ bins[bins_to_replace] = np.nan
244
+
245
+ # stop = datetime.now()
246
+ # print(f"Done Building replacement grid cells (dt={stop-start1})", flush=True)
247
+ # print(f"Done Processing spiral step (dt={stop-start0})", flush=True)
248
+
249
+ return new_cells, nbins_to_replace
250
+
251
+ @staticmethod
252
+ def _visualize_logged_stats(stats_str):
253
+ from matplotlib import pyplot as plt
254
+
255
+ stats = [[y.strip() for y in x.split(" ") if y] for x in stats_str.split("\n")]
256
+ stats.pop(1) # Remove column underline row
257
+ stats = np.array(stats)
258
+ index = pd.Index(stats[1:, 0].astype(int), name="Step")
259
+ n_replaced = stats[1:, 1].astype(int)
260
+
261
+ dt = pd.to_timedelta(stats[1:, 2]).total_seconds()
262
+ dt_unit = "s"
263
+ if dt.max() > 60:
264
+ dt /= 60
265
+ dt_unit = "m"
266
+ if dt.max() > 60:
267
+ dt /= 60
268
+ dt_unit = "H"
269
+ if dt.max() > 24:
270
+ dt /= 24
271
+ dt_unit = "D"
272
+
273
+ dt_key = f"Elapsed [{dt_unit}]"
274
+ stats = pd.DataFrame({dt_key: dt, "N Divisions": n_replaced}, index=index)
275
+
276
+ # stats = pd.Series(stats[1:, 1].astype(int), index=stats[1:, 0].astype(int), name=stats[0, 1])
277
+ # stats.index.name = stats[0, 0]
278
+
279
+ fig, ax = plt.subplots()
280
+ tax = ax.twinx()
281
+
282
+ x = stats.index
283
+ k = f"Elapsed [{dt_unit}]"
284
+ ax.plot(x, stats.loc[:, k], label=k, marker="+", ms=8)
285
+
286
+ k = "N Divisions"
287
+ tax.plot(x, stats.loc[:, k], label=k, c="C1", ls="--", marker="x", ms=8)
288
+
289
+ tax.grid(False)
290
+ ax.set_xlabel("Step Number")
291
+ ax.set_ylabel(dt_key)
292
+ tax.set_ylabel("N Divisions")
293
+
294
+ h0, l0 = ax.get_legend_handles_labels()
295
+ h1, l1 = tax.get_legend_handles_labels()
296
+
297
+ ax.legend(
298
+ h0 + h1,
299
+ l0 + l1,
300
+ title=rf"$\Delta t = {stats.loc[:, dt_key].sum():.0f} \, {dt_unit}$",
301
+ )
302
+
303
+ ax.set_yscale("log")
304
+ tax.set_yscale("log")
305
+
306
+ return ax, tax, stats
307
+
308
+ def generate_mesh(self):
309
+ logger = logging.getLogger("__main__")
310
+ start = datetime.now()
311
+ logger.warning(f"Generating {self.__class__.__name__} at {start}")
312
+
313
+ x = self.data.x.values
314
+ y = self.data.y.values
315
+
316
+ min_per_bin = self.min_per_bin
317
+ # max_bins = int(1e5)
318
+
319
+ initial_bins = self.initialize_bins()
320
+
321
+ # To reduce memory needs, only process data in mesh.
322
+ x0 = initial_bins[:, 0].min()
323
+ x1 = initial_bins[:, 1].max()
324
+ y0 = initial_bins[:, 2].min()
325
+ y1 = initial_bins[:, 3].max()
326
+ tk_data_in_mesh = (
327
+ (x0 <= x)
328
+ & (x <= x1)
329
+ & (y0 <= y)
330
+ & (y <= y1)
331
+ & np.isfinite(x)
332
+ & np.isfinite(y)
333
+ )
334
+ x = x[tk_data_in_mesh]
335
+ y = y[tk_data_in_mesh]
336
+
337
+ initial_cell_count = get_counts_per_bin(initial_bins, x, y)
338
+ # initial_cell_count = self.get_counts_per_bin_loop(initial_bins, x, y)
339
+ bins_to_replace = initial_cell_count > min_per_bin
340
+ nbins_to_replace = bins_to_replace.sum()
341
+
342
+ # raise ValueError
343
+
344
+ list_of_bins = [initial_bins]
345
+ active_bins = initial_bins
346
+
347
+ logger.warning(
348
+ """
349
+ Step N Elapsed Time
350
+ ====== ======= =============="""
351
+ )
352
+ step_start = datetime.now()
353
+ step = 0
354
+ while nbins_to_replace > 0:
355
+ active_bins, nbins_to_replace = self.process_one_spiral_step(
356
+ active_bins, x, y, min_per_bin
357
+ )
358
+ now = datetime.now()
359
+ # if not(step % 10):
360
+ logger.warning(f"{step:>6} {nbins_to_replace:>7} {(now - step_start)}")
361
+ list_of_bins.append(active_bins)
362
+ step += 1
363
+ step_start = now
364
+
365
+ list_of_bins = [b for b in list_of_bins if b is not None]
366
+ final_bins = np.vstack(list_of_bins)
367
+ valid_bins = np.isfinite(final_bins).all(axis=1)
368
+ final_bins = final_bins[valid_bins]
369
+
370
+ stop = datetime.now()
371
+ # logger.warning(f"Complete at {stop}")
372
+ logger.warning(f"\nCompleted {self.__class__.__name__} at {stop}")
373
+ logger.warning(f"Elasped time {stop - start}")
374
+ logger.warning(f"Split bin threshold {min_per_bin}")
375
+ logger.warning(
376
+ f"Generated {final_bins.shape[0]} bins for {x.size} spectra (~{x.size / final_bins.shape[0]:.3f} spectra per bin)\n"
377
+ )
378
+
379
+ self._mesh = final_bins
380
+
381
+ # return final_bins
382
+
383
+ def calculate_bin_number(self):
384
+ logger = logging.getLogger(__name__)
385
+ logger.warning(
386
+ f"Calculating {self.__class__.__name__} bin_number at {datetime.now()}"
387
+ )
388
+ x = self.data.loc[:, "x"].values
389
+ y = self.data.loc[:, "y"].values
390
+ mesh = self.mesh
391
+ nbins = mesh.shape[0]
392
+
393
+ start = datetime.now()
394
+ zbin, fill, bin_visited = calculate_bin_number_with_numba(mesh, x, y)
395
+ stop = datetime.now()
396
+
397
+ logger.warning(f"Elapsed time {stop - start}")
398
+
399
+ # return calculate_bin_number_with_numba_broadcast(mesh, x, y, fill)
400
+
401
+ # if ( verbose > 0 and
402
+ # (i % verbose == 0) ):
403
+ # print(i+1, end=", ")
404
+
405
+ if (zbin == fill).any():
406
+ # if (zbin < 0).any():
407
+ # pdb.set_trace()
408
+ logger.warning(
409
+ f"""`zbin` contains {(zbin == fill).sum()} ({100 * (zbin == fill).mean():.1f}%) fill values that are outside of mesh.
410
+ They will be replaced by NaNs and excluded from the aggregation.
411
+ """
412
+ )
413
+ # raise ValueError(msg % (zbin == fill).sum())
414
+
415
+ # Set fill bin to zero
416
+ is_fill = zbin == fill
417
+ # zbin[~is_fill] += 1
418
+ # zbin[is_fill] = -1
419
+ # print(zbin.min())
420
+ # zbin += 1
421
+ # print(zbin.min())
422
+ # `minlength=nbins` forces us to include empty bins at the end of the array.
423
+ bin_frequency = np.bincount(zbin[~is_fill], minlength=nbins)
424
+ n_empty = (bin_frequency == 0).sum()
425
+ logger.warning(
426
+ f"""Largest bin population is {bin_frequency.max()}
427
+ {n_empty} of {nbins} bins ({100 * n_empty / nbins:.1f}%) are empty
428
+ """
429
+ )
430
+
431
+ if not bin_visited.all():
432
+ logger.warning(f"{(~bin_visited).sum()} bins went unvisited.")
433
+ if (bin_visited > 1).any():
434
+ logger.warning(f"({(bin_visited > 1).sum()} bins visted more than once.")
435
+
436
+ if nbins - bin_frequency.shape[0] != 0:
437
+ raise ValueError(
438
+ f"{nbins - bin_frequency.shape[0]} mesh cells do not have an associated z-value"
439
+ )
440
+
441
+ # zbin = _pd.Series(zbin, index=self.data.index, name="zbin")
442
+ # # Pandas groupby will treat NaN as not belonging to a bin.
443
+ # zbin.replace(fill, _np.nan, inplace=True)
444
+ bin_id = SpiralMeshBinID(zbin, fill, bin_visited)
445
+ self._bin_id = bin_id
446
+ return bin_id
447
+
448
+ def place_spectra_in_mesh(self):
449
+ self.generate_mesh()
450
+ bin_id = self.calculate_bin_number()
451
+ return bin_id
452
+
453
+ def build_cat(self):
454
+ bin_id = self.bin_id.id
455
+ fill = self.bin_id.fill
456
+
457
+ # Integer number corresponds to the order over
458
+ # which the mesh was traversed.
459
+ cat = pd.Categorical(bin_id, ordered=False)
460
+ if fill in bin_id:
461
+ cat.remove_categories(fill, inplace=True)
462
+
463
+ self._cat = cat
464
+
465
+
466
+ class SpiralPlot2D(base.PlotWithZdata, base.CbarMaker):
467
+ r"""2D spiral plotting with adaptive mesh refinement.
468
+
469
+ Examples
470
+ --------
471
+ splot = SpiralPlot2D(...)
472
+ splot.initialize_mesh()
473
+ """
474
+
475
+ def __init__(
476
+ self, x, y, z=None, logx=False, logy=False, initial_bins=5, clip_data=False
477
+ ):
478
+ super().__init__()
479
+ self.set_log(x=logx, y=logy)
480
+ self.set_data(x, y, z, clip_data)
481
+ self.set_labels(x="x", y="y", z=labels_module.Count() if z is None else "z")
482
+ self.calc_initial_bins(initial_bins)
483
+ self.set_clim(None, None)
484
+
485
+ @property
486
+ def clim(self):
487
+ return self._clim
488
+
489
+ @property
490
+ def initial_bins(self):
491
+ return dict(self._initial_bins)
492
+
493
+ @property
494
+ def grouped(self):
495
+ return self._grouped
496
+
497
+ @property
498
+ def mesh(self):
499
+ return self._mesh
500
+
501
+ def agg(self, fcn=None):
502
+ r"""Aggregate the z-values into their bins."""
503
+ self.logger.debug("aggregating z-data")
504
+
505
+ # start = datetime.now()
506
+ # self.logger.warning(f"Start {start}")
507
+
508
+ if fcn is None:
509
+ if self.data.loc[:, "z"].unique().size == 1:
510
+ fcn = "count"
511
+ else:
512
+ fcn = "mean"
513
+
514
+ gb = self.grouped
515
+ agg = gb.agg(fcn)
516
+
517
+ c0, c1 = self.clim
518
+ if c0 is not None or c1 is not None:
519
+ cnt = gb.agg("count")
520
+ tk = pd.Series(True, index=agg.index)
521
+
522
+ if c0 is not None:
523
+ tk = tk & (cnt >= c0)
524
+ if c1 is not None:
525
+ tk = tk & (cnt <= c1)
526
+
527
+ agg = agg.where(tk)
528
+
529
+ # reindex to ensure we have a z-value for every bin.
530
+ reindex = pd.RangeIndex(start=0, stop=self.mesh.mesh.shape[0], step=1)
531
+ agg = agg.reindex(reindex)
532
+
533
+ cell_filter = self.mesh.cell_filter
534
+ if agg.shape != cell_filter.shape:
535
+ raise ValueError(
536
+ f"""Unable to algin `agg` and `cell_filter.
537
+ agg : {agg.shape}
538
+ filter : {cell_filter.shape}"""
539
+ )
540
+ # pdb.set_trace()
541
+ agg = agg.where(cell_filter, axis=0)
542
+
543
+ # stop = datetime.now()
544
+ # self.logger.warning(f"Stop {stop}")
545
+ # self.logger.warning(f"Elapsed {stop - start}")
546
+
547
+ return agg
548
+
549
+ def build_grouped(self):
550
+ cat = self.mesh.cat
551
+ z = self.data.loc[:, "z"]
552
+
553
+ if not (cat.size == z.size):
554
+ raise ValueError(
555
+ f"""`cat` must have same size as data's first dimesion
556
+ cat : {cat.size}
557
+ data : {z.size}
558
+ """
559
+ )
560
+
561
+ gb = z.groupby(cat)
562
+ self._grouped = gb
563
+
564
+ def calc_initial_bins(self, nbins):
565
+ data = self.data
566
+ keys = ("x", "y")
567
+ bins = {}
568
+
569
+ if isinstance(nbins, int):
570
+ # Single paramter for `nbins`.
571
+ nbins = {k: nbins for k in keys}
572
+
573
+ elif len(nbins) == len(keys):
574
+ # Passed one bin spec per axis
575
+ nbins = {k: v for k, v in zip(keys, nbins)}
576
+
577
+ else:
578
+ msg = f"Unrecognized `nbins`\ntype: {type(nbins)}\n bins:{nbins}"
579
+ raise ValueError(msg)
580
+
581
+ for k, b in nbins.items():
582
+ # Numpy and Astropy don't like NaNs when calculating bins.
583
+ # Infinities in bins (typically from log10(0)) also create problems.
584
+ d = data.loc[:, k].replace([-np.inf, np.inf], np.nan).dropna()
585
+
586
+ if not isinstance(b, (int, np.ndarray)):
587
+ raise TypeError("Only want in integer or np.ndarrays for initial edges")
588
+
589
+ if isinstance(b, int):
590
+ # Lets calculate the following quantiles.
591
+ b = np.quantile(
592
+ d, np.linspace(0, 1, b + 1)
593
+ ) # Need N + 1 edges to make N bins.
594
+
595
+ # Extend the right most bin by the larger of 1% or 0.01 (in the case of zero)
596
+ # So that y < y1 inludes data at real data edge.
597
+ b[-1] = np.max([0.01, 1.01 * b.max()])
598
+
599
+ assert not np.isnan(b).any()
600
+
601
+ bins[k] = b
602
+
603
+ bins = tuple(bins.items())
604
+ self._initial_bins = bins
605
+ return bins
606
+
607
+ def initialize_mesh(self, **kwargs):
608
+ x = self.data.loc[:, "x"]
609
+ y = self.data.loc[:, "y"]
610
+
611
+ # if self.log.x:
612
+ # x = x.apply(np.log10)
613
+ # if self.log.y:
614
+ # y = y.apply(np.log10)
615
+
616
+ xbins = self.initial_bins["x"]
617
+ ybins = self.initial_bins["y"]
618
+
619
+ mesh = SpiralMesh(x, y, xbins, ybins, **kwargs)
620
+ # Attach mesh before anything else.
621
+ # Makes debugging easier.
622
+ self._mesh = mesh
623
+
624
+ mesh.place_spectra_in_mesh()
625
+ mesh.build_cat()
626
+
627
+ def set_clim(self, lower=None, upper=None):
628
+ """Set the min (lower) and max (upper) counts per bin.
629
+
630
+ This limit is applied after the :py:meth:`groupby.agg` is run."""
631
+ assert isinstance(lower, Number) or lower is None
632
+ assert isinstance(upper, Number) or upper is None
633
+ self._clim = base.RangeLimits(lower, upper)
634
+
635
+ def set_data(self, x, y, z, clip):
636
+ super().set_data(x, y, z, clip)
637
+ data = self.data
638
+ if self.log.x:
639
+ data.loc[:, "x"] = np.log10(np.abs(data.loc[:, "x"]))
640
+ if self.log.y:
641
+ data.loc[:, "y"] = np.log10(np.abs(data.loc[:, "y"]))
642
+ self._data = data
643
+
644
+ def _limit_color_norm(self, norm):
645
+ pct = self.data.loc[:, "z"].quantile([0.01, 0.99])
646
+ v0 = pct.loc[0.01]
647
+ v1 = pct.loc[0.99]
648
+ if norm.vmin is None:
649
+ norm.vmin = v0
650
+ if norm.vmax is None:
651
+ norm.vmax = v1
652
+ norm.clip = True
653
+
654
+ def make_plot(
655
+ self,
656
+ ax=None,
657
+ cbar=True,
658
+ limit_color_norm=False,
659
+ cbar_kwargs=None,
660
+ fcn=None,
661
+ alpha_fcn=None,
662
+ **kwargs,
663
+ ):
664
+
665
+ # start = datetime.now()
666
+ # self.logger.warning("Making plot")
667
+ # self.logger.warning(f"Start {start}")
668
+
669
+ if ax is None:
670
+ fig, ax = plt.subplots()
671
+
672
+ C = self.agg(fcn=fcn)
673
+ C = np.ma.masked_invalid(C.values)
674
+ assert isinstance(C, np.ndarray)
675
+ assert C.ndim == 1
676
+ if C.shape[0] != self.mesh.mesh.shape[0]:
677
+ raise ValueError(
678
+ f"""{self.mesh.mesh.shape[0] - C.shape[0]} mesh cells do not have a z-value associated with them. The z-values and mesh are not properly aligned."""
679
+ )
680
+
681
+ xmesh = self.mesh.mesh[:, [0, 1]]
682
+ ymesh = self.mesh.mesh[:, [2, 3]]
683
+
684
+ if self.log.x:
685
+ xmesh = 10.0**xmesh
686
+ if self.log.y:
687
+ ymesh = 10.0**ymesh
688
+
689
+ # (x,y) of bin's lower left corner.
690
+ xy = zip(xmesh[:, 0], ymesh[:, 0])
691
+ dx = xmesh[:, 1] - xmesh[:, 0]
692
+ dy = ymesh[:, 1] - ymesh[:, 0]
693
+
694
+ start1 = datetime.now()
695
+ self.logger.warning("Making patches")
696
+ self.logger.warning(f"Start {start1}")
697
+
698
+ patches = [
699
+ mpl.patches.Rectangle(this_xy, this_dx, this_dy)
700
+ for this_xy, this_dx, this_dy in zip(xy, dx, dy)
701
+ ]
702
+
703
+ stop1 = datetime.now()
704
+ self.logger.warning(f"Stop {stop1}")
705
+ self.logger.warning(f"Elapsed {stop1 - start1}")
706
+
707
+ # TODO: `match_original=False` if calculate alpha for each patch.
708
+ edgecolors = "none"
709
+ linewidth = 0.0
710
+ collection = mpl.collections.PatchCollection(
711
+ patches, linewidth=linewidth, edgecolors=edgecolors
712
+ )
713
+
714
+ collection.set_array(C)
715
+
716
+ cmap = kwargs.pop("cmap", None)
717
+ norm = kwargs.pop("norm", None)
718
+ if len(kwargs):
719
+ raise ValueError(f"Unexpected kwargs {kwargs.keys()}")
720
+ # assert not kwargs
721
+
722
+ if limit_color_norm and norm is not None:
723
+ self._limit_color_norm(norm)
724
+
725
+ collection.set_alpha(None)
726
+ collection.set_cmap(cmap)
727
+ collection.set_norm(norm)
728
+ collection.autoscale_None()
729
+
730
+ ax.add_collection(collection, autolim=False)
731
+
732
+ minx = xmesh[:, 0].min()
733
+ miny = ymesh[:, 0].min()
734
+ maxx = xmesh[:, 1].max()
735
+ maxy = ymesh[:, 1].max()
736
+ collection.sticky_edges.x[:] = [minx, maxx]
737
+ collection.sticky_edges.y[:] = [miny, maxy]
738
+ corners = (minx, miny), (maxx, maxy)
739
+ ax.update_datalim(corners)
740
+ ax.autoscale_view()
741
+
742
+ cbar_or_mappable = collection
743
+ if cbar:
744
+ if cbar_kwargs is None:
745
+ cbar_kwargs = dict()
746
+
747
+ if "cax" not in cbar_kwargs.keys() and "ax" not in cbar_kwargs.keys():
748
+ cbar_kwargs["ax"] = ax
749
+
750
+ cbar = self._make_cbar(collection, norm=norm, **cbar_kwargs)
751
+ cbar_or_mappable = cbar
752
+
753
+ self._format_axis(ax)
754
+
755
+ if alpha_fcn is not None:
756
+ alpha_agg = np.ma.masked_invalid(self.agg(fcn=alpha_fcn).values)
757
+ # Feature scale then invert so smallest STD
758
+ # is most opaque.
759
+ alpha_agg = mpl.colors.Normalize()(alpha_agg)
760
+ alpha = 1 - alpha_agg
761
+ self.logger.warning("Scaling alpha filter as alpha**0.25")
762
+ alpha = alpha**0.25
763
+
764
+ # Set masked values to zero. Otherwise, masked
765
+ # values are rendered as black.
766
+ alpha = alpha.filled(0)
767
+
768
+ # Must draw to initialize `facecolor`s
769
+ plt.draw()
770
+ colors = collection.get_facecolors()
771
+ colors[:, 3] = alpha
772
+ collection.set_facecolor(colors)
773
+
774
+ # stop = datetime.now()
775
+ # self.logger.warning(f"Stop {stop}")
776
+ # self.logger.warning(f"Elapsed {stop - start}")
777
+
778
+ return ax, cbar_or_mappable
779
+
780
+ def _verify_contour_passthrough_kwargs(
781
+ self, ax, clabel_kwargs, edges_kwargs, cbar_kwargs
782
+ ):
783
+ if clabel_kwargs is None:
784
+ clabel_kwargs = dict()
785
+ if edges_kwargs is None:
786
+ edges_kwargs = dict()
787
+ if cbar_kwargs is None:
788
+ cbar_kwargs = dict()
789
+ if "cax" not in cbar_kwargs.keys() and "ax" not in cbar_kwargs.keys():
790
+ cbar_kwargs["ax"] = ax
791
+
792
+ return clabel_kwargs, edges_kwargs, cbar_kwargs
793
+
794
+ def plot_contours(
795
+ self,
796
+ ax=None,
797
+ label_levels=True,
798
+ cbar=True,
799
+ limit_color_norm=False,
800
+ cbar_kwargs=None,
801
+ fcn=None,
802
+ plot_edges=False,
803
+ edges_kwargs=None,
804
+ clabel_kwargs=None,
805
+ skip_max_clbl=True,
806
+ use_contourf=False,
807
+ # gaussian_filter_std=0,
808
+ # gaussian_filter_kwargs=None,
809
+ **kwargs,
810
+ ):
811
+ """Make a contour plot on `ax` using `ax.contour`.
812
+
813
+ Parameters
814
+ ----------
815
+ ax: mpl.axes.Axes, None
816
+ If None, create an `Axes` instance from `plt.subplots`.
817
+ label_levels: bool
818
+ If True, add labels to contours with `ax.clabel`.
819
+ cbar: bool
820
+ If True, create color bar with `labels.z`.
821
+ limit_color_norm: bool
822
+ If True, limit the color range to 0.001 and 0.999 percentile range
823
+ of the z-value, count or otherwise.
824
+ cbar_kwargs: dict, None
825
+ If not None, kwargs passed to `self._make_cbar`.
826
+ fcn: FunctionType, None
827
+ Aggregation function. If None, automatically select in :py:meth:`agg`.
828
+ plot_edges: bool
829
+ If True, plot the smoothed, extreme edges of the 2D histogram.
830
+ clabel_kwargs: None, dict
831
+ If not None, dictionary of kwargs passed to `ax.clabel`.
832
+ skip_max_clbl: bool
833
+ If True, don't label the maximum contour. Primarily used when the maximum
834
+ contour is, effectively, a point.
835
+ maximum_color:
836
+ The color for the maximum of the PDF.
837
+ use_contourf: bool
838
+ If True, use `ax.contourf`. Else use `ax.contour`.
839
+ gaussian_filter_std: int
840
+ If > 0, apply `scipy.ndimage.gaussian_filter` to the z-values using the
841
+ standard deviation specified by `gaussian_filter_std`.
842
+ gaussian_filter_kwargs: None, dict
843
+ If not None and gaussian_filter_std > 0, passed to :py:meth:`scipy.ndimage.gaussian_filter`
844
+ kwargs:
845
+ Passed to :py:meth:`ax.pcolormesh`.
846
+ If row or column normalized data, `norm` defaults to `mpl.colors.Normalize(0, 1)`.
847
+ """
848
+ levels = kwargs.pop("levels", None)
849
+ cmap = kwargs.pop("cmap", None)
850
+ norm = kwargs.pop(
851
+ "norm",
852
+ None,
853
+ # mpl.colors.BoundaryNorm(np.linspace(0, 1, 11), 256, clip=True)
854
+ # if self.axnorm in ("c", "r")
855
+ # else None,
856
+ )
857
+ linestyles = kwargs.pop(
858
+ "linestyles",
859
+ [
860
+ "-",
861
+ ":",
862
+ "--",
863
+ (0, (7, 3, 1, 3, 1, 3, 1, 3, 1, 3)),
864
+ "--",
865
+ ":",
866
+ "-",
867
+ (0, (7, 3, 1, 3, 1, 3)),
868
+ ],
869
+ )
870
+
871
+ if ax is None:
872
+ fig, ax = plt.subplots()
873
+
874
+ (
875
+ clabel_kwargs,
876
+ edges_kwargs,
877
+ cbar_kwargs,
878
+ ) = self._verify_contour_passthrough_kwargs(
879
+ ax, clabel_kwargs, edges_kwargs, cbar_kwargs
880
+ )
881
+
882
+ inline = clabel_kwargs.pop("inline", True)
883
+ inline_spacing = clabel_kwargs.pop("inline_spacing", -3)
884
+ fmt = clabel_kwargs.pop("fmt", "%s")
885
+
886
+ if ax is None:
887
+ fig, ax = plt.subplots()
888
+
889
+ C = self.agg(fcn=fcn).values
890
+ assert isinstance(C, np.ndarray)
891
+ assert C.ndim == 1
892
+ if C.shape[0] != self.mesh.mesh.shape[0]:
893
+ raise ValueError(
894
+ f"""{self.mesh.mesh.shape[0] - C.shape[0]} mesh cells do not have a z-value associated with them. The z-values and mesh are not properly aligned."""
895
+ )
896
+
897
+ x = self.mesh.mesh[:, [0, 1]].mean(axis=1)
898
+ y = self.mesh.mesh[:, [2, 3]].mean(axis=1)
899
+
900
+ if self.log.x:
901
+ x = 10.0**x
902
+ if self.log.y:
903
+ y = 10.0**y
904
+
905
+ tk_finite = np.isfinite(C)
906
+ x = x[tk_finite]
907
+ y = y[tk_finite]
908
+ C = C[tk_finite]
909
+
910
+ contour_fcn = ax.tricontour
911
+ if use_contourf:
912
+ contour_fcn = ax.tricontourf
913
+
914
+ if levels is None:
915
+ args = [x, y, C]
916
+ else:
917
+ args = [x, y, C, levels]
918
+
919
+ qset = contour_fcn(*args, linestyles=linestyles, cmap=cmap, norm=norm, **kwargs)
920
+
921
+ try:
922
+ args = (qset, levels[:-1] if skip_max_clbl else levels)
923
+ except TypeError:
924
+ # None can't be subscripted.
925
+ args = (qset,)
926
+
927
+ class nf(float):
928
+ # Source: https://matplotlib.org/3.1.0/gallery/images_contours_and_fields/contour_label_demo.html
929
+ # Define a class that forces representation of float to look a certain way
930
+ # This remove trailing zero so '1.0' becomes '1'
931
+ def __repr__(self):
932
+ return str(self).rstrip("0")
933
+
934
+ lbls = None
935
+ if label_levels:
936
+ qset.levels = [nf(level) for level in qset.levels]
937
+ lbls = ax.clabel(
938
+ *args,
939
+ inline=inline,
940
+ inline_spacing=inline_spacing,
941
+ fmt=fmt,
942
+ **clabel_kwargs,
943
+ )
944
+
945
+ cbar_or_mappable = qset
946
+ if cbar:
947
+ # Pass `norm` to `self._make_cbar` so that we can choose the ticks to use.
948
+ cbar = self._make_cbar(qset, norm=norm, **cbar_kwargs)
949
+ cbar_or_mappable = cbar
950
+
951
+ self._format_axis(ax)
952
+
953
+ return ax, lbls, cbar_or_mappable, qset
954
+
955
+
956
+ # def plot_surface(self):
957
+ #
958
+ # from scipy.interpolate import griddata
959
+ #
960
+ # z = self.agg()
961
+ # x = self.mesh.mesh[:, [0, 1]].mean(axis=1)
962
+ # y = self.mesh.mesh[:, [2, 3]].mean(axis=1)
963
+ #
964
+ # is_finite = np.isfinite(z)
965
+ # z = z[is_finite]
966
+ # x = x[is_finite]
967
+ # y = y[is_finite]
968
+ #
969
+ # xi = np.linspace(x.min(), x.max(), 100)
970
+ # yi = np.linspace(y.min(), y.max(), 100)
971
+ # # VERY IMPORTANT, to tell matplotlib how is your data organized
972
+ # zi = griddata((x, y), y, (xi[None, :], yi[:, None]), method="cubic")
973
+ #
974
+ # if ax is None:
975
+ # fig = plt.figure(figsize=(8, 8))
976
+ # ax = fig.add_subplot(projection="3d")
977
+ #
978
+ # xig, yig = np.meshgrid(xi, yi)
979
+ #
980
+ # ax.plot_surface(xx, yy, zz, cmap="Spectral_r", norm=chavp.norms.vsw)