solarwindpy 0.0.1.dev0__py3-none-any.whl → 0.1.0__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.
- plans/.velocity/metrics.json +96 -0
- plans/0-overview-template.md +268 -0
- plans/N-phase-template.md +106 -0
- plans/PLAN_AUDIT_SUMMARY.md +173 -0
- plans/TEMPLATE-USAGE-GUIDE.md +198 -0
- plans/__init__.py +1 -0
- plans/abandoned/compaction-agent-system/0-Overview.md +123 -0
- plans/abandoned/compaction-agent-system/agents-index-update-plan.md +109 -0
- plans/abandoned/compaction-agent-system/compacted_state.md +85 -0
- plans/abandoned/compaction-agent-system/implementation-plan.md +107 -0
- plans/abandoned/compaction-agent-system/system-validation-report.md +159 -0
- plans/abandoned/compaction-agent-system/usage-guide.md +210 -0
- plans/abandoned/hook-system-enhancement/0-Overview.md +214 -0
- plans/abandoned/hook-system-enhancement/1-Phase1-Core-Infrastructure.md +313 -0
- plans/abandoned/hook-system-enhancement/2-Phase2-Intelligent-Testing.md +385 -0
- plans/abandoned/hook-system-enhancement/3-Phase3-Physics-Validation.md +444 -0
- plans/abandoned/hook-system-enhancement/4-Phase4-Performance-Monitoring.md +458 -0
- plans/abandoned/hook-system-enhancement/5-Phase5-Developer-Experience.md +532 -0
- plans/abandoned/hook-system-enhancement/6-Implementation-Timeline.md +274 -0
- plans/abandoned/hook-system-enhancement/7-Risk-Management.md +376 -0
- plans/abandoned/hook-system-enhancement/8-Testing-Strategy.md +579 -0
- plans/abandoned/readthedocs-automation/0-Overview.md +247 -0
- plans/abandoned/readthedocs-automation/1-Emergency-Documentation-Fixes.md +270 -0
- plans/abandoned/readthedocs-automation/2-Template-System-Enhancement.md +811 -0
- plans/abandoned/readthedocs-automation/3-Quality-Audit-ReadTheDocs-Integration.md +844 -0
- plans/abandoned/readthedocs-automation/4-Plan-Consolidation-Cleanup.md +632 -0
- plans/abandoned/readthedocs-automation/9-Closeout.md +207 -0
- plans/abandoned/readthedocs-automation/ABANDONMENT_REASON.md +72 -0
- plans/cicd-architecture-redesign/0-Overview.md +193 -0
- plans/cicd-architecture-redesign/1-Workflow-Creation.md +103 -0
- plans/cicd-architecture-redesign/2-Version-Detection.md +123 -0
- plans/cicd-architecture-redesign/3-Deployment-Gates.md +169 -0
- plans/cicd-architecture-redesign/4-RC-Testing.md +194 -0
- plans/cicd-architecture-redesign/5-TestPyPI-Validation.md +264 -0
- plans/cicd-architecture-redesign/6-Production-Release.md +263 -0
- plans/cicd-architecture-redesign/7-Cleanup.md +243 -0
- plans/cicd-architecture-redesign/8-Documentation.md +285 -0
- plans/cicd-architecture-redesign/Closeout.md +225 -0
- plans/closeout-template.md +259 -0
- plans/completed/circular-import-audit/0-Overview.md +152 -0
- plans/completed/circular-import-audit/1-Static-Dependency-Analysis.md +62 -0
- plans/completed/circular-import-audit/2-Dynamic-Import-Testing.md +56 -0
- plans/completed/circular-import-audit/3-Performance-Impact-Assessment.md +56 -0
- plans/completed/circular-import-audit/4-Issue-Remediation.md +78 -0
- plans/completed/circular-import-audit/5-Preventive-Infrastructure.md +89 -0
- plans/completed/claude-settings-ecosystem-alignment/0-Overview.md +162 -0
- plans/completed/claude-settings-ecosystem-alignment/1-Security-Foundation.md +148 -0
- plans/completed/claude-settings-ecosystem-alignment/2-Hook-Integration.md +158 -0
- plans/completed/claude-settings-ecosystem-alignment/3-Agent-System-Integration.md +177 -0
- plans/completed/claude-settings-ecosystem-alignment/4-Enhanced-Workflow-Automation.md +159 -0
- plans/completed/claude-settings-ecosystem-alignment/5-Validation-Monitoring.md +181 -0
- plans/completed/claude-settings-ecosystem-alignment/compacted_session_state.md +290 -0
- plans/completed/combined_plan_with_checklist_documentation/1-Overview-and-Goals.md +51 -0
- plans/completed/combined_plan_with_checklist_documentation/2-Toolchain-and-Hosting.md +69 -0
- plans/completed/combined_plan_with_checklist_documentation/3-Repository-Structure.md +61 -0
- plans/completed/combined_plan_with_checklist_documentation/4-Configuration-and-Standards.md +70 -0
- plans/completed/combined_plan_with_checklist_documentation/5-Documentation-Content.md +62 -0
- plans/completed/combined_plan_with_checklist_documentation/6-CI-CD-and-Validation.md +58 -0
- plans/completed/combined_plan_with_checklist_documentation/7-Maintenance.md +55 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/0-Overview.md +135 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/1-Common-fixtures.md +59 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/10-power_laws.md +56 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/2-core.py-FitFunction.md +118 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/3-gaussians.py-Gaussian-GaussianNormalized-GaussianLn.md +69 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/4-trend_fits.py-TrendFit.md +99 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/5-plots.py-FFPlot.md +98 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/6-tex_info.py-TeXinfo.md +79 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/7-Justification.md +49 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/8-exponentials.md +64 -0
- plans/completed/combined_test_plan_with_checklist_fitfunctions/9-lines.md +58 -0
- plans/completed/combined_test_plan_with_checklist_plotting/0-Overview.md +142 -0
- plans/completed/combined_test_plan_with_checklist_plotting/1-base.py.md +90 -0
- plans/completed/combined_test_plan_with_checklist_plotting/10-labels-special.py.md +102 -0
- plans/completed/combined_test_plan_with_checklist_plotting/11-labels-chemistry.py.md +212 -0
- plans/completed/combined_test_plan_with_checklist_plotting/12-labels-composition.py.md +242 -0
- plans/completed/combined_test_plan_with_checklist_plotting/13-labels-datetime.py.md +247 -0
- plans/completed/combined_test_plan_with_checklist_plotting/14-labels-elemental_abundance.py.md +274 -0
- plans/completed/combined_test_plan_with_checklist_plotting/15-visual-validation.md +256 -0
- plans/completed/combined_test_plan_with_checklist_plotting/16-integration-testing.md +266 -0
- plans/completed/combined_test_plan_with_checklist_plotting/17-performance-benchmarks.md +267 -0
- plans/completed/combined_test_plan_with_checklist_plotting/18-Fixtures-and-Utilities.md +86 -0
- plans/completed/combined_test_plan_with_checklist_plotting/2-agg_plot.py.md +90 -0
- plans/completed/combined_test_plan_with_checklist_plotting/3-histograms.py.md +201 -0
- plans/completed/combined_test_plan_with_checklist_plotting/4-scatter.py.md +167 -0
- plans/completed/combined_test_plan_with_checklist_plotting/5-spiral.py.md +216 -0
- plans/completed/combined_test_plan_with_checklist_plotting/6-orbits.py.md +108 -0
- plans/completed/combined_test_plan_with_checklist_plotting/7-tools.py.md +86 -0
- plans/completed/combined_test_plan_with_checklist_plotting/8-select_data_from_figure.py.md +97 -0
- plans/completed/combined_test_plan_with_checklist_plotting/9-labels-base.py.md +88 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/.gitkeep +0 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/0-Overview.md +170 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/1-Package-Entry-Point-__init__.py.md +121 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/2-Core-Base-Classes-base.py.md +142 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/3-Plotting-Helpers-plots.py.md +123 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/4-LISIRD-Sub-package.md +119 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/5-Extrema-Calculator.md +103 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/6-Sunspot-Number-Sub-package.md +163 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/7-Sunspot-Number-Init.py.md +217 -0
- plans/completed/combined_test_plan_with_checklist_solar_activity/compacted_state.md +52 -0
- plans/completed/compaction-agent-modernization/0-Overview.md +156 -0
- plans/completed/compaction-agent-modernization/1-Architecture-Audit-Gap-Analysis.md +132 -0
- plans/completed/compaction-agent-modernization/2-Token-Baseline-Recalibration.md +153 -0
- plans/completed/compaction-agent-modernization/3-Agent-Reference-Updates.md +184 -0
- plans/completed/compaction-agent-modernization/4-Compression-Algorithm-Modernization.md +238 -0
- plans/completed/compaction-agent-modernization/5-Workflow-Integration-Streamlining.md +252 -0
- plans/completed/compaction-agent-modernization/6-Template-Structure-Optimization.md +240 -0
- plans/completed/compaction-agent-modernization/7-Integration-Testing-Validation.md +292 -0
- plans/completed/compaction-hook-enhancement/0-Overview.md +150 -0
- plans/completed/compaction-hook-enhancement/1-Token-Estimation-Enhancement.md +179 -0
- plans/completed/compaction-hook-enhancement/2-Compression-Intelligence.md +294 -0
- plans/completed/compaction-hook-enhancement/3-Git-Integration-Metadata.md +310 -0
- plans/completed/compaction-hook-enhancement/4-Session-Continuity-Features.md +358 -0
- plans/completed/compaction-hook-enhancement/5-Testing-Strategy.md +404 -0
- plans/completed/compaction-hook-enhancement/6-Integration-Roadmap.md +319 -0
- plans/completed/compaction-hook-enhancement/compacted_state.md +142 -0
- plans/completed/docstring-audit-enhancement/0-Overview.md +274 -0
- plans/completed/docstring-audit-enhancement/1-Infrastructure-Setup-and-Validation-Tools.md +206 -0
- plans/completed/docstring-audit-enhancement/2-Core-Physics-Modules-Enhancement.md +237 -0
- plans/completed/docstring-audit-enhancement/3-Fitfunctions-Mathematical-Modules-Enhancement.md +188 -0
- plans/completed/docstring-audit-enhancement/4-Plotting-Visualization-Modules-Enhancement.md +243 -0
- plans/completed/docstring-audit-enhancement/5-Specialized-Modules-Enhancement.md +216 -0
- plans/completed/docstring-audit-enhancement/6-Validation-and-Integration.md +216 -0
- plans/completed/fitfunctions-testing-implementation/0-Overview.md +130 -0
- plans/completed/fitfunctions-testing-implementation/1-Test-Infrastructure-Setup.md +79 -0
- plans/completed/fitfunctions-testing-implementation/2-Common-Fixtures-Test-Utilities.md +104 -0
- plans/completed/fitfunctions-testing-implementation/3-Core-FitFunction-Testing.md +168 -0
- plans/completed/fitfunctions-testing-implementation/4-Specialized-Function-Classes.md +210 -0
- plans/completed/fitfunctions-testing-implementation/5-Advanced-Classes-Testing.md +214 -0
- plans/completed/fitfunctions-testing-implementation/6-Plotting-Integration-Testing.md +231 -0
- plans/completed/fitfunctions-testing-implementation/7-Extended-Coverage-BONUS.md +184 -0
- plans/completed/numpy-docstring-conversion-plan/numpy-docstring-conversion-plan.md +118 -0
- plans/completed/pr-review-remediation/0-Overview.md +138 -0
- plans/completed/pr-review-remediation/1-Critical-Safety-Improvements.md +179 -0
- plans/completed/pr-review-remediation/2-Smart-Timeouts-Validation.md +399 -0
- plans/completed/pr-review-remediation/3-Enhanced-GitHub-Integration.md +258 -0
- plans/completed/pr-review-remediation/compacted_state.md +66 -0
- plans/completed/python-310-migration/0-Overview.md +390 -0
- plans/completed/python-310-migration/1-Planning-Setup.md +164 -0
- plans/completed/python-310-migration/2-Implementation.md +256 -0
- plans/completed/python-310-migration/3-Testing-Validation.md +335 -0
- plans/completed/python-310-migration/4-Documentation-Release.md +274 -0
- plans/completed/python-310-migration/5-Closeout.md +252 -0
- plans/completed/requirements-management-consolidation/0-Overview.md +118 -0
- plans/completed/requirements-management-consolidation/1-Documentation-Validation-Environment-Setup.md +116 -0
- plans/completed/requirements-management-consolidation/2-Requirements-Consolidation.md +161 -0
- plans/completed/requirements-management-consolidation/3-Workflow-Automation-Final-Integration.md +196 -0
- plans/completed/single-ecosystem-plan-implementation/0-Overview.md +83 -0
- plans/completed/single-ecosystem-plan-implementation/1-Plan-Preservation-Session-Management.md +38 -0
- plans/completed/single-ecosystem-plan-implementation/2-File-Structure-Optimization.md +43 -0
- plans/completed/single-ecosystem-plan-implementation/3-Plan-Migration-Archive-Setup.md +82 -0
- plans/completed/single-ecosystem-plan-implementation/4-Agent-System-Transformation.md +108 -0
- plans/completed/single-ecosystem-plan-implementation/5-Template-System-Enhancement.md +131 -0
- plans/completed/single-ecosystem-plan-implementation/6-Final-Validation-Testing.md +120 -0
- plans/completed/test-directory-consolidation/0-Overview.md +51 -0
- plans/completed/test-directory-consolidation/1-Structure-Preparation.md +82 -0
- plans/completed/test-directory-consolidation/2-File-Migration.md +100 -0
- plans/completed/test-directory-consolidation/3-Import-Transformation.md +117 -0
- plans/completed/test-directory-consolidation/4-Configuration-Consolidation.md +140 -0
- plans/completed/test-directory-consolidation/5-Validation.md +152 -0
- plans/completed/test-directory-consolidation/6-Cleanup.md +156 -0
- plans/completed/test-planning-agents-architecture/0-Overview.md +79 -0
- plans/completed/test-planning-agents-architecture/1-Branch-Isolation-Testing.md +49 -0
- plans/completed/test-planning-agents-architecture/2-Cross-Branch-Coordination.md +51 -0
- plans/completed/test-planning-agents-architecture/3-Merge-Workflow-Testing.md +48 -0
- plans/deployment-semver-pypi-rtd/0-Overview.md +463 -0
- plans/deployment-semver-pypi-rtd/1-Semantic-Versioning-Foundation.md +136 -0
- plans/deployment-semver-pypi-rtd/2-PyPI-Deployment-Infrastructure.md +168 -0
- plans/deployment-semver-pypi-rtd/3-Release-Automation.md +214 -0
- plans/deployment-semver-pypi-rtd/4-Plan-Closeout.md +543 -0
- plans/deployment-semver-pypi-rtd/compacted_session_state.md +172 -0
- plans/deployment-semver-pypi-rtd/compacted_state.md +131 -0
- plans/documentation-code-audit/0-Overview.md +393 -0
- plans/documentation-code-audit/1-Discovery-Inventory.md +183 -0
- plans/documentation-code-audit/2-Execution-Environment-Setup.md +263 -0
- plans/documentation-code-audit/3-Systematic-Validation.md +322 -0
- plans/documentation-code-audit/4-Code-Example-Remediation.md +358 -0
- plans/documentation-code-audit/5-Physics-MultiIndex-Compliance.md +464 -0
- plans/documentation-code-audit/6-Doctest-Integration.md +523 -0
- plans/documentation-code-audit/7-Reporting-Documentation.md +498 -0
- plans/documentation-code-audit/8-Closeout.md +456 -0
- plans/documentation-rebuild-session/compacted_state.md +109 -0
- plans/documentation-rendering-fixes/0-Overview.md +104 -0
- plans/documentation-rendering-fixes/1-Sphinx-Build-Diagnostics-Warning-Audit.md +101 -0
- plans/documentation-rendering-fixes/2-Configuration-Infrastructure-Fixes.md +113 -0
- plans/documentation-rendering-fixes/3-Docstring-Syntax-Audit-Repair.md +131 -0
- plans/documentation-rendering-fixes/4-HTML-Page-Rendering-Verification.md +113 -0
- plans/documentation-rendering-fixes/5-Advanced-Documentation-Quality-Assurance.md +119 -0
- plans/documentation-rendering-fixes/6-Documentation-Build-Optimization-Testing.md +129 -0
- plans/documentation-rendering-fixes/compacted_state.md +132 -0
- plans/documentation-template-fix/0-Overview.md +197 -0
- plans/documentation-template-fix/1-Template-System-Analysis.md +269 -0
- plans/documentation-template-fix/2-Template-Modification.md +609 -0
- plans/documentation-template-fix/3-Build-System-Integration.md +766 -0
- plans/documentation-template-fix/4-Testing-Validation.md +1399 -0
- plans/documentation-template-fix/5-Documentation-Training.md +602 -0
- plans/documentation-workflow-fix/0-Overview.md +222 -0
- plans/documentation-workflow-fix/1-Immediate-Fixes.md +238 -0
- plans/documentation-workflow-fix/2-Configuration-Setup.md +298 -0
- plans/documentation-workflow-fix/3-Pre-commit-Integration.md +382 -0
- plans/documentation-workflow-fix/4-Workflow-Improvements.md +446 -0
- plans/documentation-workflow-fix/5-Documentation-and-Training.md +527 -0
- plans/duplicate-object-warnings-fix-plan.md +130 -0
- plans/github-issues-migration/0-Overview.md +510 -0
- plans/github-issues-migration/1-Foundation-Label-System.md +180 -0
- plans/github-issues-migration/2-Migration-Tool-Rewrite.md +235 -0
- plans/github-issues-migration/3-CLI-Integration-Automation.md +169 -0
- plans/github-issues-migration/4-Validated-Migration.md +252 -0
- plans/github-issues-migration/5-Documentation-Training.md +171 -0
- plans/github-issues-migration/6-Closeout.md +179 -0
- plans/github-workflows-repair/repair-plan.md +299 -0
- plans/issues_from_plans.py +342 -0
- plans/pr-270-doc-validation-fixes/0-Overview.md +354 -0
- plans/pr-270-doc-validation-fixes/1-Critical-PR-Fixes.md +117 -0
- plans/pr-270-doc-validation-fixes/2-Framework-Right-Sizing.md +129 -0
- plans/pr-270-doc-validation-fixes/3-Sustainable-Documentation.md +126 -0
- plans/pr-270-doc-validation-fixes/4-Closeout-Migration.md +143 -0
- plans/pr-270-doc-validation-fixes/PLAN_COMPLETED.md +149 -0
- plans/python-310-migration/0-Overview.md +390 -0
- plans/python-310-migration/1-Planning-Setup.md +164 -0
- plans/python-310-migration/2-Implementation.md +256 -0
- plans/python-310-migration/3-Testing-Validation.md +335 -0
- plans/python-310-migration/4-Documentation-Release.md +274 -0
- plans/python-310-migration/5-Closeout.md +252 -0
- plans/readthedocs-simplified/0-Overview.md +243 -0
- plans/readthedocs-simplified/1-Immediate-Fixes.md +216 -0
- plans/readthedocs-simplified/2-Template-Simplification.md +278 -0
- plans/readthedocs-simplified/3-ReadTheDocs-Setup.md +298 -0
- plans/readthedocs-simplified/4-Testing-Validation.md +328 -0
- plans/readthedocs-simplified/5-Closeout.md +231 -0
- plans/readthedocs-simplified/compacted_state.md +127 -0
- plans/session-compaction-2025-08-12/compacted_state.md +114 -0
- plans/session-compaction-2025-08-13/compacted_state.md +145 -0
- plans/session-continuity-protocol/0-Overview.md +35 -0
- plans/session-continuity-protocol/1-Core-Principles-Framework.md +40 -0
- plans/session-continuity-protocol/2-Pre-Session-Validation-System.md +79 -0
- plans/session-continuity-protocol/3-Context-Switching-Prevention.md +87 -0
- plans/session-continuity-protocol/4-Progress-Tracking-Recovery.md +100 -0
- plans/sphinx-warnings-analysis.md +222 -0
- plans/systemprompt-optimization/0-Overview.md +447 -0
- plans/systemprompt-optimization/1-Deploy-SystemPrompt.md +114 -0
- plans/systemprompt-optimization/2-Documentation-Alignment.md +198 -0
- plans/systemprompt-optimization/3-Monitoring-Infrastructure.md +396 -0
- plans/systemprompt-optimization/4-Implementation-Script.md +450 -0
- plans/systemprompt-optimization/9-Closeout.md +165 -0
- plans/systemprompt-optimization/compacted_state.md +143 -0
- plans/template-value-propositions/0-Overview.md +357 -0
- plans/template-value-propositions/1-Value-Proposition-Framework-Design.md +144 -0
- plans/template-value-propositions/2-Plan-Template-Enhancement.md +178 -0
- plans/template-value-propositions/3-Value-Generator-Hook-Implementation.md +291 -0
- plans/template-value-propositions/4-Value-Validator-Hook-Implementation.md +274 -0
- plans/template-value-propositions/5-Documentation-Agent-Updates.md +219 -0
- plans/template-value-propositions/6-Integration-Testing-Validation.md +247 -0
- plans/tests-audit/0-Overview.md +410 -0
- plans/tests-audit/1-Discovery-Inventory.md +170 -0
- plans/tests-audit/2-Physics-Validation-Audit.md +195 -0
- plans/tests-audit/3-Architecture-Compliance.md +195 -0
- plans/tests-audit/4-Numerical-Stability-Analysis.md +203 -0
- plans/tests-audit/5-Documentation-Enhancement.md +220 -0
- plans/tests-audit/6-Audit-Deliverables.md +220 -0
- plans/tests-audit/7-Closeout.md +252 -0
- plans/tests-audit/artifacts/ARCHITECTURE_COMPLIANCE_REPORT.md +315 -0
- plans/tests-audit/artifacts/ARCHITECTURE_RECOMMENDATIONS.md +943 -0
- plans/tests-audit/artifacts/COMPREHENSIVE_AUDIT_REPORT.md +356 -0
- plans/tests-audit/artifacts/CONTRIBUTING_ENHANCED_TEMPLATE.md +419 -0
- plans/tests-audit/artifacts/COVERAGE_GAP_ANALYSIS.md +152 -0
- plans/tests-audit/artifacts/DOCUMENTATION_ENHANCEMENT_REPORT.md +502 -0
- plans/tests-audit/artifacts/EXECUTIVE_AUDIT_SUMMARY.md +129 -0
- plans/tests-audit/artifacts/IMPLEMENTATION_ROADMAP.md +647 -0
- plans/tests-audit/artifacts/NUMERICAL_RECOMMENDATIONS.md +739 -0
- plans/tests-audit/artifacts/NUMERICAL_STABILITY_GUIDE_TEMPLATE.rst +451 -0
- plans/tests-audit/artifacts/NUMERICAL_STABILITY_REPORT.md +301 -0
- plans/tests-audit/artifacts/PHASE_3_SUMMARY.md +280 -0
- plans/tests-audit/artifacts/PHASE_4_SUMMARY.md +229 -0
- plans/tests-audit/artifacts/PHASE_5_SUMMARY.md +292 -0
- plans/tests-audit/artifacts/PHASE_6_CLOSEOUT.md +278 -0
- plans/tests-audit/artifacts/PHYSICS_GUIDE_TEMPLATE.rst +268 -0
- plans/tests-audit/artifacts/PHYSICS_VALIDATION_REPORT.md +235 -0
- plans/tests-audit/artifacts/TECHNICAL_DELIVERABLES_PACKAGE.md +2502 -0
- plans/tests-audit/artifacts/TEST_INVENTORY.csv +1204 -0
- plans/tests-audit/artifacts/TEST_INVENTORY.md +135 -0
- plans/tests-audit/artifacts/test_discovery_analysis.py +231 -0
- plans/tests-audit/artifacts/test_parser.py +395 -0
- solarwindpy/README.md +3 -0
- solarwindpy/Untitled.ipynb +54 -0
- solarwindpy/__init__.py +74 -0
- solarwindpy/core/__init__.py +23 -0
- solarwindpy/core/alfvenic_turbulence.py +804 -0
- solarwindpy/core/base.py +267 -0
- solarwindpy/core/ions.py +309 -0
- solarwindpy/core/plasma.py +2133 -0
- solarwindpy/core/spacecraft.py +256 -0
- solarwindpy/core/tensor.py +90 -0
- solarwindpy/core/units_constants.py +199 -0
- solarwindpy/core/vector.py +328 -0
- solarwindpy/fitfunctions/__init__.py +20 -0
- solarwindpy/fitfunctions/core.py +734 -0
- solarwindpy/fitfunctions/exponentials.py +188 -0
- solarwindpy/fitfunctions/gaussians.py +264 -0
- solarwindpy/fitfunctions/lines.py +116 -0
- solarwindpy/fitfunctions/moyal.py +71 -0
- solarwindpy/fitfunctions/plots.py +751 -0
- solarwindpy/fitfunctions/power_laws.py +209 -0
- solarwindpy/fitfunctions/tex_info.py +568 -0
- solarwindpy/fitfunctions/trend_fits.py +482 -0
- solarwindpy/instabilities/__init__.py +16 -0
- solarwindpy/instabilities/beta_ani.py +82 -0
- solarwindpy/instabilities/verscharen2016.py +631 -0
- solarwindpy/plotting/__init__.py +33 -0
- solarwindpy/plotting/agg_plot.py +489 -0
- solarwindpy/plotting/base.py +465 -0
- solarwindpy/plotting/hist1d.py +405 -0
- solarwindpy/plotting/hist2d.py +1035 -0
- solarwindpy/plotting/histograms.py +1845 -0
- solarwindpy/plotting/labels/__init__.py +104 -0
- solarwindpy/plotting/labels/base.py +686 -0
- solarwindpy/plotting/labels/chemistry.py +19 -0
- solarwindpy/plotting/labels/composition.py +100 -0
- solarwindpy/plotting/labels/datetime.py +235 -0
- solarwindpy/plotting/labels/elemental_abundance.py +73 -0
- solarwindpy/plotting/labels/special.py +794 -0
- solarwindpy/plotting/orbits.py +515 -0
- solarwindpy/plotting/scatter.py +99 -0
- solarwindpy/plotting/select_data_from_figure.py +329 -0
- solarwindpy/plotting/spiral.py +980 -0
- solarwindpy/plotting/tools.py +434 -0
- solarwindpy/scripts/__init__.py +1 -0
- solarwindpy/scripts/logs/.gitignore +1 -0
- solarwindpy/solar_activity/__init__.py +53 -0
- solarwindpy/solar_activity/base.py +605 -0
- solarwindpy/solar_activity/lisird/__init__.py +3 -0
- solarwindpy/solar_activity/lisird/extrema_calculator.py +394 -0
- solarwindpy/solar_activity/lisird/lisird.py +319 -0
- solarwindpy/solar_activity/plots.py +116 -0
- solarwindpy/solar_activity/sunspot_number/.DS_Store +0 -0
- solarwindpy/solar_activity/sunspot_number/__init__.py +3 -0
- solarwindpy/solar_activity/sunspot_number/sidc.py +556 -0
- solarwindpy/solar_activity/sunspot_number/ssn_extrema.csv +72 -0
- solarwindpy/solar_activity/sunspot_number/ssn_extrema.csv.silso +72 -0
- solarwindpy/tools/__init__.py +162 -0
- solarwindpy-0.1.0.dist-info/METADATA +181 -0
- solarwindpy-0.1.0.dist-info/RECORD +409 -0
- {solarwindpy-0.0.1.dev0.dist-info → solarwindpy-0.1.0.dist-info}/WHEEL +1 -1
- solarwindpy-0.1.0.dist-info/licenses/LICENSE.rst +32 -0
- solarwindpy-0.1.0.dist-info/top_level.txt +3 -0
- tests/__init__.py +1 -0
- tests/conftest.py +10 -0
- tests/core/__init__.py +1 -0
- tests/core/test_alfvenic_turbulence.py +544 -0
- tests/core/test_base.py +112 -0
- tests/core/test_base_head_tail.py +29 -0
- tests/core/test_base_mi_tuples.py +11 -0
- tests/core/test_core_verify_datetimeindex.py +32 -0
- tests/core/test_ions.py +325 -0
- tests/core/test_plasma.py +2581 -0
- tests/core/test_plasma_io.py +12 -0
- tests/core/test_quantities.py +507 -0
- tests/core/test_spacecraft.py +210 -0
- tests/core/test_units_constants.py +22 -0
- tests/data/epoch.csv +4 -0
- tests/data/plasma.csv +4 -0
- tests/data/spacecraft.csv +4 -0
- tests/fitfunctions/conftest.py +60 -0
- tests/fitfunctions/test_core.py +193 -0
- tests/fitfunctions/test_exponentials.py +342 -0
- tests/fitfunctions/test_gaussians.py +142 -0
- tests/fitfunctions/test_lines.py +349 -0
- tests/fitfunctions/test_moyal.py +258 -0
- tests/fitfunctions/test_plots.py +258 -0
- tests/fitfunctions/test_power_laws.py +365 -0
- tests/fitfunctions/test_tex_info.py +183 -0
- tests/fitfunctions/test_trend_fit_properties.py +31 -0
- tests/fitfunctions/test_trend_fits.py +244 -0
- tests/plotting/__init__.py +1 -0
- tests/plotting/labels/__init__.py +1 -0
- tests/plotting/labels/test_chemistry.py +243 -0
- tests/plotting/labels/test_composition.py +345 -0
- tests/plotting/labels/test_datetime.py +445 -0
- tests/plotting/labels/test_elemental_abundance.py +366 -0
- tests/plotting/labels/test_init.py +66 -0
- tests/plotting/labels/test_labels_base.py +347 -0
- tests/plotting/labels/test_special.py +550 -0
- tests/plotting/test_agg_plot.py +602 -0
- tests/plotting/test_base.py +752 -0
- tests/plotting/test_fixtures_utilities.py +775 -0
- tests/plotting/test_histograms.py +546 -0
- tests/plotting/test_integration.py +675 -0
- tests/plotting/test_orbits.py +435 -0
- tests/plotting/test_performance.py +708 -0
- tests/plotting/test_scatter.py +752 -0
- tests/plotting/test_select_data_from_figure.py +1209 -0
- tests/plotting/test_spiral.py +573 -0
- tests/plotting/test_tools.py +607 -0
- tests/plotting/test_visual_validation.py +465 -0
- tests/solar_activity/__init__.py +1 -0
- tests/solar_activity/lisird/__init__.py +1 -0
- tests/solar_activity/lisird/test_extrema_calculator.py +593 -0
- tests/solar_activity/lisird/test_lisird_id.py +187 -0
- tests/solar_activity/sunspot_number/__init__.py +1 -0
- tests/solar_activity/sunspot_number/test_init.py +399 -0
- tests/solar_activity/sunspot_number/test_sidc.py +465 -0
- tests/solar_activity/sunspot_number/test_sidc_id.py +223 -0
- tests/solar_activity/sunspot_number/test_sidc_loader.py +275 -0
- tests/solar_activity/sunspot_number/test_ssn_extrema.py +406 -0
- tests/solar_activity/test_base.py +656 -0
- tests/solar_activity/test_init.py +396 -0
- tests/solar_activity/test_plots.py +371 -0
- tests/test_circular_imports.py +408 -0
- tests/test_issue_titles.py +25 -0
- tests/test_statusline.py +298 -0
- solarwindpy-0.0.1.dev0.dist-info/METADATA +0 -14
- solarwindpy-0.0.1.dev0.dist-info/RECORD +0 -4
- solarwindpy-0.0.1.dev0.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
"""Base classes for solar activity indicators."""
|
|
2
|
+
|
|
3
|
+
import pdb # noqa: F401
|
|
4
|
+
import logging
|
|
5
|
+
import re
|
|
6
|
+
import urllib
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from abc import ABC, abstractmethod, abstractproperty, abstractstaticmethod
|
|
13
|
+
from collections import namedtuple
|
|
14
|
+
|
|
15
|
+
from scipy.interpolate import InterpolatedUnivariateSpline
|
|
16
|
+
|
|
17
|
+
_Loader_Dtypes_Columns = namedtuple("LoaderNamedTuple", ["dtypes", "columns"])
|
|
18
|
+
_data_date_pattern = re.compile(r"\d{4}\d{2}\d{2}")
|
|
19
|
+
|
|
20
|
+
pd.set_option("mode.chained_assignment", "raise")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Base(ABC):
|
|
24
|
+
"""Abstract base class providing a logger interface."""
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def logger(self):
|
|
28
|
+
"""``logging.Logger`` attached to the instance."""
|
|
29
|
+
return self._logger
|
|
30
|
+
|
|
31
|
+
def _init_logger(self):
|
|
32
|
+
logger = logging.getLogger(
|
|
33
|
+
name="{}.{}".format(__name__, self.__class__.__name__)
|
|
34
|
+
)
|
|
35
|
+
self._logger = logger
|
|
36
|
+
|
|
37
|
+
def __str__(self):
|
|
38
|
+
return self.__class__.__name__
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ID(Base):
|
|
42
|
+
"""Container for identifying a particular data product."""
|
|
43
|
+
|
|
44
|
+
def __init__(self, key: str) -> None:
|
|
45
|
+
"""Instantiate the identifier and set the corresponding URL.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
key : str
|
|
50
|
+
Key that maps to a URL fragment in :py:attr:`_trans_url`.
|
|
51
|
+
"""
|
|
52
|
+
self._init_logger()
|
|
53
|
+
self.set_key(key)
|
|
54
|
+
|
|
55
|
+
@abstractproperty
|
|
56
|
+
def _url_base(self):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
@abstractproperty
|
|
60
|
+
def _trans_url(self):
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def key(self):
|
|
65
|
+
return self._key
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def url(self):
|
|
69
|
+
r"""URL specifying location from which data was downloaded."""
|
|
70
|
+
return self._url
|
|
71
|
+
|
|
72
|
+
def set_key(self, key):
|
|
73
|
+
"""Set the identifier key and construct the download URL."""
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
url_end = self._trans_url[key]
|
|
77
|
+
except KeyError:
|
|
78
|
+
msg = "{} key unavailable".format(key)
|
|
79
|
+
self.logger.exception(msg)
|
|
80
|
+
raise NotImplementedError(msg)
|
|
81
|
+
|
|
82
|
+
url = urllib.parse.urljoin(self._url_base, url_end)
|
|
83
|
+
|
|
84
|
+
self.logger.info("Setting key\nkey : %s\nurl : %s", key, url)
|
|
85
|
+
|
|
86
|
+
self._key = key
|
|
87
|
+
self._url = url
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class DataLoader(Base):
|
|
91
|
+
def __init__(self, key, url):
|
|
92
|
+
r"""Initialize a data loader.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
key : str
|
|
97
|
+
Unique data identifier, typically from something like
|
|
98
|
+
:class:`SIDC_ID`.
|
|
99
|
+
url : str
|
|
100
|
+
Full download URL for the data source.
|
|
101
|
+
"""
|
|
102
|
+
self._init_logger()
|
|
103
|
+
self.set_key(key)
|
|
104
|
+
self.set_url(url)
|
|
105
|
+
self.get_data_ctime()
|
|
106
|
+
self.get_data_age()
|
|
107
|
+
|
|
108
|
+
@abstractproperty
|
|
109
|
+
def data_path(self):
|
|
110
|
+
# return Path(__file__).parent / "data"
|
|
111
|
+
return Path.home() / "solarwindpy" / "data"
|
|
112
|
+
|
|
113
|
+
@abstractstaticmethod
|
|
114
|
+
def convert_nans(data):
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
@abstractmethod
|
|
118
|
+
def download_data(self):
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
@abstractmethod
|
|
122
|
+
def load_data(self):
|
|
123
|
+
self.logger.info(f"""Loading {self.key!s} data""")
|
|
124
|
+
|
|
125
|
+
self.maybe_update_stale_data()
|
|
126
|
+
|
|
127
|
+
today = pd.to_datetime("today").strftime("%Y%m%d")
|
|
128
|
+
fpath = (self.data_path / today).with_suffix(".csv")
|
|
129
|
+
data = pd.read_csv(fpath, index_col=0, header=0)
|
|
130
|
+
|
|
131
|
+
data.set_index(pd.DatetimeIndex(data.index), inplace=True)
|
|
132
|
+
self._data = data
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def logger(self):
|
|
136
|
+
return self._logger
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def data(self):
|
|
140
|
+
return self._data
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def key(self):
|
|
144
|
+
return self._key
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def url(self):
|
|
148
|
+
return self._url
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def ctime(self):
|
|
152
|
+
return self._ctime
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def age(self):
|
|
156
|
+
return self._age
|
|
157
|
+
|
|
158
|
+
def _init_logger(self):
|
|
159
|
+
logger = logging.getLogger(
|
|
160
|
+
name="{}.{}".format(__name__, self.__class__.__name__)
|
|
161
|
+
)
|
|
162
|
+
self._logger = logger
|
|
163
|
+
|
|
164
|
+
def set_key(self, key):
|
|
165
|
+
self._key = key
|
|
166
|
+
|
|
167
|
+
def set_url(self, new):
|
|
168
|
+
self._url = new
|
|
169
|
+
|
|
170
|
+
def get_data_ctime(self):
|
|
171
|
+
r"""Determine when the current data set was created.
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
pandas.Timestamp
|
|
176
|
+
Creation time inferred from the file name, or ``1970-01-01`` if no
|
|
177
|
+
prior data are found.
|
|
178
|
+
"""
|
|
179
|
+
path = self.data_path
|
|
180
|
+
files = [str(x) for x in path.rglob("*.csv")]
|
|
181
|
+
dates = [re.findall(_data_date_pattern, f) for f in files]
|
|
182
|
+
|
|
183
|
+
if not len(dates):
|
|
184
|
+
# This will automatically return a date in 1970, so we
|
|
185
|
+
# will clear and download new data.
|
|
186
|
+
self._ctime = pd.to_datetime(0)
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
dates = np.concatenate(dates)
|
|
190
|
+
dates = [x.strip("/") for x in dates]
|
|
191
|
+
dates = np.unique(dates)
|
|
192
|
+
|
|
193
|
+
# BUG: This fails if there are more than two dated data directories.
|
|
194
|
+
# if dates.size > 1:
|
|
195
|
+
# raise ValueError("Too many dates: %s" % ", ".join(dates.astype(str)))
|
|
196
|
+
# elif not dates.size:
|
|
197
|
+
# ctime = pd.to_datetime(0)
|
|
198
|
+
# else:
|
|
199
|
+
# ctime = pd.to_datetime(dates[0])
|
|
200
|
+
|
|
201
|
+
assert dates.size == 1
|
|
202
|
+
ctime = pd.to_datetime(dates[0])
|
|
203
|
+
|
|
204
|
+
self.logger.info("Local data ctime %s", ctime)
|
|
205
|
+
self._ctime = ctime
|
|
206
|
+
|
|
207
|
+
def get_data_age(self):
|
|
208
|
+
ctime = self.ctime
|
|
209
|
+
today = pd.to_datetime("today")
|
|
210
|
+
dt = today - ctime
|
|
211
|
+
self._data_age = dt
|
|
212
|
+
|
|
213
|
+
def maybe_update_stale_data(self):
|
|
214
|
+
r"""Download new data if the existing cache is stale."""
|
|
215
|
+
self.logger.info("Updating stale data")
|
|
216
|
+
|
|
217
|
+
old_ctime = self.ctime
|
|
218
|
+
new_ctime = pd.to_datetime("today")
|
|
219
|
+
|
|
220
|
+
if new_ctime.date() > old_ctime.date():
|
|
221
|
+
old_data_path = self.data_path / old_ctime.strftime("%Y%m%d")
|
|
222
|
+
new_data_path = self.data_path / new_ctime.strftime("%Y%m%d")
|
|
223
|
+
new_data_path.parent.mkdir(parents=True, exist_ok=True)
|
|
224
|
+
|
|
225
|
+
self.download_data(new_data_path, old_data_path)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class ActivityIndicator(Base):
|
|
229
|
+
@property
|
|
230
|
+
def id(self):
|
|
231
|
+
return self._id
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def loader(self):
|
|
235
|
+
return self._loader
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def data(self):
|
|
239
|
+
r"""Shortcut to `self.loader.data`."""
|
|
240
|
+
return self.loader.data
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def extrema(self):
|
|
244
|
+
return self._extrema
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def norm_by(self):
|
|
248
|
+
try:
|
|
249
|
+
return self._norm_by
|
|
250
|
+
except AttributeError:
|
|
251
|
+
raise AttributeError("Please calculate normalized quantity")
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def interpolated(self):
|
|
255
|
+
return self._interpolated
|
|
256
|
+
|
|
257
|
+
def set_id(self, new):
|
|
258
|
+
assert isinstance(new, ID)
|
|
259
|
+
self._id = new
|
|
260
|
+
|
|
261
|
+
# Need to store `interpolated` in subclass. Allows normalized ssn to be interpolated.
|
|
262
|
+
@abstractmethod
|
|
263
|
+
def interpolate_data(self, source_data, target_index):
|
|
264
|
+
"""Interpolate ``source_data`` onto ``target_index``.
|
|
265
|
+
|
|
266
|
+
Parameters
|
|
267
|
+
----------
|
|
268
|
+
source_data : pandas.Series or pandas.DataFrame
|
|
269
|
+
Data with a :class:`~pandas.DatetimeIndex` to interpolate.
|
|
270
|
+
target_index : pandas.DatetimeIndex
|
|
271
|
+
Target time axis for the interpolation.
|
|
272
|
+
|
|
273
|
+
Returns
|
|
274
|
+
-------
|
|
275
|
+
pandas.DataFrame
|
|
276
|
+
Data interpolated onto ``target_index``.
|
|
277
|
+
"""
|
|
278
|
+
assert isinstance(target_index, pd.DatetimeIndex)
|
|
279
|
+
assert isinstance(source_data.index, pd.DatetimeIndex)
|
|
280
|
+
|
|
281
|
+
if isinstance(source_data, pd.Series):
|
|
282
|
+
source_data = source_data.to_frame()
|
|
283
|
+
|
|
284
|
+
nans = source_data.isna().any().any()
|
|
285
|
+
if nans:
|
|
286
|
+
raise NotImplementedError(
|
|
287
|
+
"You must drop NaNs in the subclass's caller before "
|
|
288
|
+
"calling parent method."
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
x_target = target_index.asi8
|
|
292
|
+
x_source = source_data.index.asi8
|
|
293
|
+
|
|
294
|
+
interpolated = {}
|
|
295
|
+
for k, y_source in source_data.items():
|
|
296
|
+
interpolator = InterpolatedUnivariateSpline(
|
|
297
|
+
x_source, y_source, check_finite=True
|
|
298
|
+
)
|
|
299
|
+
interped = interpolator(x_target)
|
|
300
|
+
interpolated[k] = interped
|
|
301
|
+
|
|
302
|
+
interpolated = pd.DataFrame(interpolated, index=target_index)
|
|
303
|
+
|
|
304
|
+
# Remove extrapolated data
|
|
305
|
+
tk = pd.Series(True, target_index)
|
|
306
|
+
|
|
307
|
+
t1 = target_index[-1]
|
|
308
|
+
s1 = source_data.index[-1]
|
|
309
|
+
if s1 < t1:
|
|
310
|
+
tk = tk & (target_index <= s1)
|
|
311
|
+
|
|
312
|
+
t0 = target_index[0]
|
|
313
|
+
s0 = source_data.index[0]
|
|
314
|
+
if t0 < s0:
|
|
315
|
+
tk = tk & (s0 <= target_index)
|
|
316
|
+
|
|
317
|
+
if not tk.all():
|
|
318
|
+
interpolated.where(tk, inplace=True, axis=0)
|
|
319
|
+
|
|
320
|
+
self._interpolated = interpolated
|
|
321
|
+
return interpolated
|
|
322
|
+
|
|
323
|
+
@abstractproperty
|
|
324
|
+
def normalized(self):
|
|
325
|
+
pass
|
|
326
|
+
|
|
327
|
+
@abstractmethod
|
|
328
|
+
def set_extrema(self):
|
|
329
|
+
pass
|
|
330
|
+
|
|
331
|
+
@abstractmethod
|
|
332
|
+
def run_normalization(self):
|
|
333
|
+
r"""Normalize the indicator within each solar cycle.
|
|
334
|
+
|
|
335
|
+
Parameters
|
|
336
|
+
----------
|
|
337
|
+
norm_by : {{"max", "zscore", "feature-scale"}}
|
|
338
|
+
Normalization algorithm to apply.
|
|
339
|
+
|
|
340
|
+
Returns
|
|
341
|
+
-------
|
|
342
|
+
pandas.Series
|
|
343
|
+
Normalized values indexed by time.
|
|
344
|
+
"""
|
|
345
|
+
pass
|
|
346
|
+
|
|
347
|
+
def _run_normalization(self, indicator, norm_fcn):
|
|
348
|
+
cut = self.extrema.cut_spec_by_interval(indicator.index, kind="Cycle")
|
|
349
|
+
joint = pd.concat(
|
|
350
|
+
[indicator, cut], axis=1, keys=["indicator", "cycle"]
|
|
351
|
+
).sort_index(axis=1)
|
|
352
|
+
grouped = joint.groupby("cycle")
|
|
353
|
+
|
|
354
|
+
normed = {}
|
|
355
|
+
for k, g in grouped:
|
|
356
|
+
g = g.loc[:, "indicator"]
|
|
357
|
+
normed[k] = norm_fcn(g)
|
|
358
|
+
normed = pd.concat(normed.values(), axis=0).sort_index()
|
|
359
|
+
return normed
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class IndicatorExtrema(Base):
|
|
363
|
+
"""Base class for objects describing indicator extrema."""
|
|
364
|
+
|
|
365
|
+
def __init__(self, *args, **kwargs):
|
|
366
|
+
self._init_logger()
|
|
367
|
+
self.load_or_set_data(*args, **kwargs)
|
|
368
|
+
self.calculate_intervals()
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def data(self):
|
|
372
|
+
return self._data
|
|
373
|
+
|
|
374
|
+
@property
|
|
375
|
+
def cycle_intervals(self):
|
|
376
|
+
r""":class:`pd.Interval` for rising and falling edges and full cycle."""
|
|
377
|
+
return self._cycle_intervals
|
|
378
|
+
|
|
379
|
+
@property
|
|
380
|
+
def extrema_bands(self):
|
|
381
|
+
r"""Bands of time (:math:`\Delta t`) about indicator extrema.
|
|
382
|
+
|
|
383
|
+
Parameters
|
|
384
|
+
----------
|
|
385
|
+
dt : str or pandas.Timedelta
|
|
386
|
+
Window half-width used in :meth:`calculate_extrema_bands`.
|
|
387
|
+
"""
|
|
388
|
+
try:
|
|
389
|
+
return self._extrema_bands
|
|
390
|
+
except AttributeError:
|
|
391
|
+
raise AttributeError("Have you called `extrema.calculate_extrema_bands`?")
|
|
392
|
+
|
|
393
|
+
@abstractmethod
|
|
394
|
+
def load_or_set_data(self):
|
|
395
|
+
pass
|
|
396
|
+
|
|
397
|
+
# path = Path(__file__).parent / "ssn_extrema.csv"
|
|
398
|
+
# data = pd.read_csv(path, header=0, skiprows=15, index_col=0)
|
|
399
|
+
# data = pd.to_datetime(data.stack(), format="%Y-%m-%d").unstack(level=1)
|
|
400
|
+
# data.columns.names = ["kind"]
|
|
401
|
+
# self._data = data
|
|
402
|
+
|
|
403
|
+
#######################################################################
|
|
404
|
+
# Tools for grouping data by Cycle and Cycle Edge (Rising or Falling) #
|
|
405
|
+
#######################################################################
|
|
406
|
+
def calculate_intervals(self):
|
|
407
|
+
r"""Compute rising, falling, and full-cycle time intervals.
|
|
408
|
+
|
|
409
|
+
Notes
|
|
410
|
+
-----
|
|
411
|
+
The rising edge comes before the falling edge in time, i.e. it's
|
|
412
|
+
Min ``N`` followed by Max ``N``. Also calculate intervals for a full
|
|
413
|
+
SSN cycle.
|
|
414
|
+
"""
|
|
415
|
+
extrema = self.data
|
|
416
|
+
intervals = pd.DataFrame(
|
|
417
|
+
index=extrema.index,
|
|
418
|
+
columns=pd.Index(["Rise", "Fall", "Cycle"], name="kind"),
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
# Make `today` only keep the date.
|
|
422
|
+
today = pd.to_datetime(pd.to_datetime("today").date())
|
|
423
|
+
for c, r in extrema.iterrows():
|
|
424
|
+
t0 = r.loc["Min"]
|
|
425
|
+
t1 = r.loc["Max"]
|
|
426
|
+
|
|
427
|
+
if pd.isna(t1):
|
|
428
|
+
# No maximum yet, then use Today for maximum
|
|
429
|
+
t1 = today
|
|
430
|
+
|
|
431
|
+
try:
|
|
432
|
+
# Get next cycle's Minimum to calculate Falling edge
|
|
433
|
+
t2 = extrema.loc[c + 1, "Min"]
|
|
434
|
+
except KeyError:
|
|
435
|
+
if t1 < today:
|
|
436
|
+
# We haven't reached next Min yet, but have current cycle Max
|
|
437
|
+
# so use today.
|
|
438
|
+
t2 = today
|
|
439
|
+
else:
|
|
440
|
+
# This cycle does not have a falling edge.
|
|
441
|
+
t2 = t1 + pd.to_timedelta(6 * 365, unit="D")
|
|
442
|
+
|
|
443
|
+
rise_ = pd.Interval(t0, t1)
|
|
444
|
+
fall_ = pd.Interval(t1, t2)
|
|
445
|
+
all_ = pd.Interval(t0, t2)
|
|
446
|
+
|
|
447
|
+
if t1 == t2:
|
|
448
|
+
# Then no falling edge
|
|
449
|
+
fall_ = pd.NaT
|
|
450
|
+
|
|
451
|
+
intervals.loc[c] = pd.Series({"Rise": rise_, "Fall": fall_, "Cycle": all_})
|
|
452
|
+
|
|
453
|
+
intervals = intervals.sort_index(axis=1)
|
|
454
|
+
self._cycle_intervals = intervals
|
|
455
|
+
return intervals
|
|
456
|
+
|
|
457
|
+
def cut_spec_by_interval(self, epoch, kind=None, tk_cycles=None):
|
|
458
|
+
r"""Assign epochs to solar-cycle intervals.
|
|
459
|
+
|
|
460
|
+
Parameters
|
|
461
|
+
----------
|
|
462
|
+
epoch : pandas.Series or pandas.DatetimeIndex
|
|
463
|
+
Data to cut.
|
|
464
|
+
kind : str, optional
|
|
465
|
+
If provided, restricts the cut to a subset of interval types.
|
|
466
|
+
|
|
467
|
+
========= ===============================
|
|
468
|
+
Key Description
|
|
469
|
+
========= ===============================
|
|
470
|
+
None Cut by all available options.
|
|
471
|
+
"Cycle" Cut by solar cycle
|
|
472
|
+
"Rise" Cut by rising edge
|
|
473
|
+
"Fall" Cut by falling edge
|
|
474
|
+
"Edges" Cut by `["Fall", "Rise"]`.
|
|
475
|
+
Exclusive option.
|
|
476
|
+
========= ===============================
|
|
477
|
+
|
|
478
|
+
Note that ``"Edges"`` is exclusive and will specify
|
|
479
|
+
``["Fall", "Rise"]`` alone.
|
|
480
|
+
tk_cycles : list or slice, optional
|
|
481
|
+
If not ``None``, a selector used to choose target solar cycles.
|
|
482
|
+
|
|
483
|
+
Returns
|
|
484
|
+
-------
|
|
485
|
+
pandas.Series
|
|
486
|
+
Series of :class:`pandas.Interval` objects labeling each epoch.
|
|
487
|
+
"""
|
|
488
|
+
if isinstance(epoch, pd.DatetimeIndex):
|
|
489
|
+
epoch = epoch.to_series()
|
|
490
|
+
|
|
491
|
+
intervals = self.cycle_intervals
|
|
492
|
+
|
|
493
|
+
available_kind = intervals.columns.get_level_values("kind")
|
|
494
|
+
if kind is None:
|
|
495
|
+
kind = available_kind
|
|
496
|
+
elif isinstance(kind, str):
|
|
497
|
+
if kind == "Edges":
|
|
498
|
+
intervals = intervals.loc[:, ["Fall", "Rise"]]
|
|
499
|
+
# kind = ["Rise", "Fall"]
|
|
500
|
+
else:
|
|
501
|
+
if kind not in available_kind:
|
|
502
|
+
raise ValueError(f"""Interval `{kind!s}` is unavailable""")
|
|
503
|
+
intervals = intervals.loc[:, [kind]]
|
|
504
|
+
# kind = [kind]
|
|
505
|
+
elif hasattr(kind, "__iter__"):
|
|
506
|
+
if not np.all([k in available_kind for k in kind]):
|
|
507
|
+
raise ValueError(f"""Interval `{kind!s}` is unavailable""")
|
|
508
|
+
intervals = intervals.loc[:, kind]
|
|
509
|
+
else:
|
|
510
|
+
raise ValueError(f"""Interval `{kind!s}` is unavailable""")
|
|
511
|
+
|
|
512
|
+
if tk_cycles is not None:
|
|
513
|
+
intervals = intervals.loc[tk_cycles]
|
|
514
|
+
|
|
515
|
+
ii = pd.IntervalIndex(intervals.stack()).sort_values()
|
|
516
|
+
if not (ii.is_unique and ii.is_monotonic_increasing):
|
|
517
|
+
raise ValueError
|
|
518
|
+
|
|
519
|
+
cut = pd.cut(epoch, ii)
|
|
520
|
+
cut.name = "Cycle_Interval"
|
|
521
|
+
|
|
522
|
+
return cut
|
|
523
|
+
|
|
524
|
+
############################################################
|
|
525
|
+
# Tools for selecting data within some dt of cycle extrema #
|
|
526
|
+
############################################################
|
|
527
|
+
def calculate_extrema_bands(self, dt="365d"):
|
|
528
|
+
r"""Return time windows around indicator extrema.
|
|
529
|
+
|
|
530
|
+
Parameters
|
|
531
|
+
----------
|
|
532
|
+
dt : str or pandas.Timedelta, optional
|
|
533
|
+
Half-width of the window around each extremum. Defaults to ``"365d"``.
|
|
534
|
+
|
|
535
|
+
Returns
|
|
536
|
+
-------
|
|
537
|
+
pandas.DataFrame
|
|
538
|
+
``Min`` and ``Max`` intervals for each cycle.
|
|
539
|
+
"""
|
|
540
|
+
dt = pd.to_timedelta(dt)
|
|
541
|
+
extrema = self.data.stack()
|
|
542
|
+
|
|
543
|
+
dt = pd.to_timedelta(dt)
|
|
544
|
+
dt = np.atleast_1d(dt)
|
|
545
|
+
if dt.size == 2:
|
|
546
|
+
dl, dr = dt
|
|
547
|
+
elif dt.size == 1:
|
|
548
|
+
dl = dr = dt[0]
|
|
549
|
+
else:
|
|
550
|
+
raise ValueError("Only know how to handle 1 or 2 dt options.")
|
|
551
|
+
|
|
552
|
+
left = extrema.subtract(dl)
|
|
553
|
+
right = extrema.add(dr)
|
|
554
|
+
lr = pd.DataFrame({"left": left, "right": right})
|
|
555
|
+
|
|
556
|
+
def make_interval(x):
|
|
557
|
+
return pd.Interval(x.loc["left"], x.loc["right"])
|
|
558
|
+
|
|
559
|
+
bands = lr.apply(make_interval, axis=1).unstack(level="kind")
|
|
560
|
+
self._extrema_bands = bands
|
|
561
|
+
return bands
|
|
562
|
+
|
|
563
|
+
def cut_about_extrema_bands(self, epoch, tk_cycles=None, kind=None):
|
|
564
|
+
r"""Bin epochs relative to extrema bands.
|
|
565
|
+
|
|
566
|
+
Computed with :py:meth:`calculate_extrema_bands`.
|
|
567
|
+
|
|
568
|
+
Parameters
|
|
569
|
+
----------
|
|
570
|
+
epoch : pandas.DatetimeIndex
|
|
571
|
+
Times to classify.
|
|
572
|
+
tk_cycles : slice, optional
|
|
573
|
+
Subset of cycles to use when cutting.
|
|
574
|
+
kind : {{"Min", "Max"}}, optional
|
|
575
|
+
Restrict the classification to minima or maxima.
|
|
576
|
+
|
|
577
|
+
Returns
|
|
578
|
+
-------
|
|
579
|
+
tuple[pandas.Series, pandas.Series]
|
|
580
|
+
A series of intervals and a mapped series of the form ``"N-Min"`` or
|
|
581
|
+
``"N-Max"``.
|
|
582
|
+
"""
|
|
583
|
+
bands = self.extrema_bands
|
|
584
|
+
if tk_cycles is not None:
|
|
585
|
+
bands = bands.loc[tk_cycles]
|
|
586
|
+
|
|
587
|
+
if kind is None:
|
|
588
|
+
kind = ["Min", "Max"]
|
|
589
|
+
|
|
590
|
+
elif isinstance(kind, str):
|
|
591
|
+
kind = [kind]
|
|
592
|
+
|
|
593
|
+
bands = bands.loc[:, kind]
|
|
594
|
+
bands = bands.stack()
|
|
595
|
+
|
|
596
|
+
# TODO: verify bands shape
|
|
597
|
+
intervals = pd.IntervalIndex(bands.values).sort_values()
|
|
598
|
+
cut = pd.cut(epoch, intervals)
|
|
599
|
+
cut = pd.Series(cut, index=epoch, name="spec_by_extrema_band")
|
|
600
|
+
|
|
601
|
+
mapper = bands.reset_index(name="Intervals").set_index("Intervals")
|
|
602
|
+
mapper = mapper.loc[:, "Number"].astype(str) + "-" + mapper.loc[:, "kind"]
|
|
603
|
+
mapped = cut.map(mapper)
|
|
604
|
+
|
|
605
|
+
return cut, mapped
|