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.
- 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.1.dist-info/METADATA +181 -0
- solarwindpy-0.1.1.dist-info/RECORD +409 -0
- {solarwindpy-0.0.1.dev0.dist-info → solarwindpy-0.1.1.dist-info}/WHEEL +1 -1
- solarwindpy-0.1.1.dist-info/licenses/LICENSE.rst +32 -0
- solarwindpy-0.1.1.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,275 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Simplified test SIDCLoader class.
|
|
3
|
+
|
|
4
|
+
This module tests the core SIDCLoader functionality from solar_activity.sunspot_number.sidc:
|
|
5
|
+
- Data conversion and NaN handling
|
|
6
|
+
- Basic inheritance and method existence
|
|
7
|
+
- URL and key handling
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
import pandas as pd
|
|
12
|
+
import numpy as np
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from unittest.mock import Mock, patch, MagicMock
|
|
15
|
+
|
|
16
|
+
from solarwindpy.solar_activity.sunspot_number.sidc import SIDCLoader, SIDC_ID
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TestSIDCLoaderCore:
|
|
20
|
+
"""Test core SIDCLoader functionality."""
|
|
21
|
+
|
|
22
|
+
def test_convert_nans_basic(self):
|
|
23
|
+
"""Test convert_nans replaces -1 with np.nan."""
|
|
24
|
+
# Create a minimal loader instance for testing convert_nans method
|
|
25
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
26
|
+
loader = SIDCLoader("m", "http://example.com")
|
|
27
|
+
|
|
28
|
+
# Create test DataFrame with -1 values
|
|
29
|
+
test_data = pd.DataFrame(
|
|
30
|
+
{
|
|
31
|
+
"ssn": [10.5, -1, 25.3, -1, 8.7],
|
|
32
|
+
"std": [2.1, 3.4, -1, 5.6, -1],
|
|
33
|
+
"n_obs": [12, 15, -1, 18, 20],
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
loader.convert_nans(test_data)
|
|
38
|
+
|
|
39
|
+
# Check that -1 values are replaced with NaN
|
|
40
|
+
assert pd.isna(test_data.loc[1, "ssn"])
|
|
41
|
+
assert pd.isna(test_data.loc[3, "ssn"])
|
|
42
|
+
assert pd.isna(test_data.loc[2, "std"])
|
|
43
|
+
assert pd.isna(test_data.loc[4, "std"])
|
|
44
|
+
assert pd.isna(test_data.loc[2, "n_obs"])
|
|
45
|
+
|
|
46
|
+
# Check that other values remain unchanged
|
|
47
|
+
assert test_data.loc[0, "ssn"] == 10.5
|
|
48
|
+
assert test_data.loc[2, "ssn"] == 25.3
|
|
49
|
+
assert test_data.loc[4, "ssn"] == 8.7
|
|
50
|
+
|
|
51
|
+
def test_convert_nans_no_minus_ones(self):
|
|
52
|
+
"""Test convert_nans when there are no -1 values."""
|
|
53
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
54
|
+
loader = SIDCLoader("m", "http://example.com")
|
|
55
|
+
|
|
56
|
+
test_data = pd.DataFrame({"ssn": [10.5, 20.3, 25.3], "std": [2.1, 3.4, 4.2]})
|
|
57
|
+
original_data = test_data.copy()
|
|
58
|
+
|
|
59
|
+
loader.convert_nans(test_data)
|
|
60
|
+
|
|
61
|
+
# Data should remain unchanged
|
|
62
|
+
pd.testing.assert_frame_equal(test_data, original_data)
|
|
63
|
+
|
|
64
|
+
def test_convert_nans_all_minus_ones(self):
|
|
65
|
+
"""Test convert_nans when all values are -1."""
|
|
66
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
67
|
+
loader = SIDCLoader("m", "http://example.com")
|
|
68
|
+
|
|
69
|
+
test_data = pd.DataFrame({"ssn": [-1, -1, -1], "std": [-1, -1, -1]})
|
|
70
|
+
|
|
71
|
+
loader.convert_nans(test_data)
|
|
72
|
+
|
|
73
|
+
# All values should be NaN
|
|
74
|
+
assert test_data.isna().all().all()
|
|
75
|
+
|
|
76
|
+
def test_inheritance_from_data_loader(self):
|
|
77
|
+
"""Test that SIDCLoader inherits from DataLoader."""
|
|
78
|
+
from solarwindpy.solar_activity.base import DataLoader
|
|
79
|
+
|
|
80
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
81
|
+
loader = SIDCLoader("m", "http://example.com")
|
|
82
|
+
|
|
83
|
+
assert isinstance(loader, DataLoader)
|
|
84
|
+
# Check that it has key properties from the base class
|
|
85
|
+
assert loader.key == "m"
|
|
86
|
+
assert loader.url == "http://example.com"
|
|
87
|
+
|
|
88
|
+
def test_data_path_property_structure(self):
|
|
89
|
+
"""Test that data_path property creates correct path structure."""
|
|
90
|
+
# Mock the parent data_path to avoid filesystem dependencies
|
|
91
|
+
with patch.object(
|
|
92
|
+
SIDCLoader.__bases__[0], "data_path", new_callable=lambda: Path("/tmp/test")
|
|
93
|
+
):
|
|
94
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
95
|
+
loader = SIDCLoader("m", "http://example.com")
|
|
96
|
+
|
|
97
|
+
expected_path = Path("/tmp/test") / "sidc" / "m"
|
|
98
|
+
assert loader.data_path == expected_path
|
|
99
|
+
|
|
100
|
+
def test_data_path_with_different_keys(self):
|
|
101
|
+
"""Test data_path property with different SIDC keys."""
|
|
102
|
+
test_keys = ["d", "m", "m13", "y", "hd", "hm", "hm13"]
|
|
103
|
+
|
|
104
|
+
for key in test_keys:
|
|
105
|
+
with patch.object(
|
|
106
|
+
SIDCLoader.__bases__[0],
|
|
107
|
+
"data_path",
|
|
108
|
+
new_callable=lambda: Path("/tmp/test"),
|
|
109
|
+
):
|
|
110
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
111
|
+
loader = SIDCLoader(key, "http://example.com")
|
|
112
|
+
expected_path = Path("/tmp/test") / "sidc" / key
|
|
113
|
+
assert loader.data_path == expected_path
|
|
114
|
+
|
|
115
|
+
def test_method_existence(self):
|
|
116
|
+
"""Test that required methods exist."""
|
|
117
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
118
|
+
loader = SIDCLoader("m", "http://example.com")
|
|
119
|
+
|
|
120
|
+
# Check that key methods exist
|
|
121
|
+
assert hasattr(loader, "convert_nans")
|
|
122
|
+
assert callable(loader.convert_nans)
|
|
123
|
+
assert hasattr(loader, "download_data")
|
|
124
|
+
assert callable(loader.download_data)
|
|
125
|
+
assert hasattr(loader, "load_data")
|
|
126
|
+
assert callable(loader.load_data)
|
|
127
|
+
assert hasattr(loader, "data_path")
|
|
128
|
+
|
|
129
|
+
@patch("pandas.read_csv")
|
|
130
|
+
def test_download_data_calls_read_csv(self, mock_read_csv, tmp_path):
|
|
131
|
+
"""Test that download_data calls pandas.read_csv."""
|
|
132
|
+
# Create a simple mock CSV response
|
|
133
|
+
mock_csv = pd.DataFrame(
|
|
134
|
+
{
|
|
135
|
+
"year": [1996],
|
|
136
|
+
"month": [1],
|
|
137
|
+
"year_fraction": [1996.042],
|
|
138
|
+
"ssn": [17.5],
|
|
139
|
+
"std": [8.0],
|
|
140
|
+
"n_obs": [14],
|
|
141
|
+
"definitive": [True],
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
mock_read_csv.return_value = mock_csv
|
|
145
|
+
|
|
146
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
147
|
+
loader = SIDCLoader("m", "http://example.com/snmtotcsv.php")
|
|
148
|
+
loader._logger = Mock() # Mock the logger to avoid logging issues
|
|
149
|
+
|
|
150
|
+
new_data_path = tmp_path / "new_data"
|
|
151
|
+
old_data_path = tmp_path / "old_data"
|
|
152
|
+
|
|
153
|
+
loader.download_data(new_data_path, old_data_path)
|
|
154
|
+
|
|
155
|
+
# Verify that read_csv was called with the URL
|
|
156
|
+
mock_read_csv.assert_called_once()
|
|
157
|
+
args, kwargs = mock_read_csv.call_args
|
|
158
|
+
assert args[0] == "http://example.com/snmtotcsv.php"
|
|
159
|
+
assert kwargs["sep"] == ";"
|
|
160
|
+
assert kwargs["header"] is None
|
|
161
|
+
|
|
162
|
+
def test_download_data_invalid_key_raises_error(self, tmp_path):
|
|
163
|
+
"""Test that invalid keys in download_data raise NotImplementedError."""
|
|
164
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
165
|
+
loader = SIDCLoader("invalid_key", "http://example.com")
|
|
166
|
+
loader._logger = Mock()
|
|
167
|
+
|
|
168
|
+
new_data_path = tmp_path / "new_data"
|
|
169
|
+
old_data_path = tmp_path / "old_data"
|
|
170
|
+
|
|
171
|
+
with pytest.raises(
|
|
172
|
+
NotImplementedError, match="You have not yet used the SSN specified"
|
|
173
|
+
):
|
|
174
|
+
loader.download_data(new_data_path, old_data_path)
|
|
175
|
+
|
|
176
|
+
@patch.object(SIDCLoader.__bases__[0], "load_data") # Mock parent load_data
|
|
177
|
+
@patch("solarwindpy.solar_activity.sunspot_number.sidc.SSNExtrema")
|
|
178
|
+
def test_load_data_calls_parent_and_ssn_extrema(
|
|
179
|
+
self, mock_ssn_extrema_class, mock_parent_load_data
|
|
180
|
+
):
|
|
181
|
+
"""Test that load_data calls parent load_data and uses SSNExtrema."""
|
|
182
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
183
|
+
loader = SIDCLoader("m", "http://example.com")
|
|
184
|
+
loader._logger = Mock()
|
|
185
|
+
|
|
186
|
+
# Create mock data for the loader
|
|
187
|
+
test_dates = pd.date_range("2008-01-01", "2010-12-31", freq="MS")
|
|
188
|
+
test_data = pd.DataFrame(
|
|
189
|
+
{
|
|
190
|
+
"ssn": np.random.uniform(10, 100, len(test_dates)),
|
|
191
|
+
"std": np.random.uniform(5, 15, len(test_dates)),
|
|
192
|
+
},
|
|
193
|
+
index=test_dates,
|
|
194
|
+
)
|
|
195
|
+
loader._data = test_data
|
|
196
|
+
|
|
197
|
+
# Mock SSNExtrema
|
|
198
|
+
mock_extrema = Mock()
|
|
199
|
+
mock_intervals = pd.DataFrame(
|
|
200
|
+
{
|
|
201
|
+
"Cycle": [
|
|
202
|
+
pd.Interval(pd.Timestamp("2008-01-01"), pd.Timestamp("2019-12-31")),
|
|
203
|
+
pd.Interval(pd.Timestamp("2020-01-01"), pd.Timestamp("2030-12-31")),
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
index=[24, 25],
|
|
207
|
+
)
|
|
208
|
+
mock_intervals.index.name = "Number"
|
|
209
|
+
mock_extrema.cycle_intervals = mock_intervals
|
|
210
|
+
mock_ssn_extrema_class.return_value = mock_extrema
|
|
211
|
+
|
|
212
|
+
loader.load_data()
|
|
213
|
+
|
|
214
|
+
# Verify parent load_data was called
|
|
215
|
+
mock_parent_load_data.assert_called_once()
|
|
216
|
+
|
|
217
|
+
# Verify SSNExtrema was instantiated
|
|
218
|
+
mock_ssn_extrema_class.assert_called_once()
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class TestSIDCLoaderEdgeCases:
|
|
222
|
+
"""Test edge cases for SIDCLoader."""
|
|
223
|
+
|
|
224
|
+
def test_convert_nans_mixed_dtypes(self):
|
|
225
|
+
"""Test convert_nans with mixed data types."""
|
|
226
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
227
|
+
loader = SIDCLoader("m", "http://example.com")
|
|
228
|
+
|
|
229
|
+
test_data = pd.DataFrame(
|
|
230
|
+
{
|
|
231
|
+
"ssn": [10.5, -1, 25.3], # float
|
|
232
|
+
"n_obs": [12, -1, 18], # int
|
|
233
|
+
"definitive": [True, False, True], # bool, shouldn't change
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
loader.convert_nans(test_data)
|
|
238
|
+
|
|
239
|
+
# Check float column
|
|
240
|
+
assert pd.isna(test_data.loc[1, "ssn"])
|
|
241
|
+
assert test_data.loc[0, "ssn"] == 10.5
|
|
242
|
+
|
|
243
|
+
# Check int column
|
|
244
|
+
assert pd.isna(test_data.loc[1, "n_obs"])
|
|
245
|
+
assert test_data.loc[0, "n_obs"] == 12
|
|
246
|
+
|
|
247
|
+
# Check bool column (should remain unchanged)
|
|
248
|
+
assert test_data.loc[0, "definitive"] == True
|
|
249
|
+
assert test_data.loc[1, "definitive"] == False
|
|
250
|
+
|
|
251
|
+
def test_initialization_with_real_sidc_id(self):
|
|
252
|
+
"""Test initialization with a real SIDC_ID object."""
|
|
253
|
+
sidc_id = SIDC_ID("m")
|
|
254
|
+
|
|
255
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
256
|
+
loader = SIDCLoader(sidc_id.key, sidc_id.url)
|
|
257
|
+
|
|
258
|
+
assert loader.key == "m"
|
|
259
|
+
assert "snmtotcsv.php" in loader.url
|
|
260
|
+
assert isinstance(loader, SIDCLoader)
|
|
261
|
+
|
|
262
|
+
def test_path_creation_does_not_create_directories(self):
|
|
263
|
+
"""Test that accessing data_path doesn't create actual directories."""
|
|
264
|
+
with patch.object(
|
|
265
|
+
SIDCLoader.__bases__[0],
|
|
266
|
+
"data_path",
|
|
267
|
+
new_callable=lambda: Path("/nonexistent/test"),
|
|
268
|
+
):
|
|
269
|
+
with patch.object(SIDCLoader, "_init_logger"):
|
|
270
|
+
loader = SIDCLoader("m", "http://example.com")
|
|
271
|
+
|
|
272
|
+
path = loader.data_path
|
|
273
|
+
# Path object should be created but directory shouldn't exist
|
|
274
|
+
assert isinstance(path, Path)
|
|
275
|
+
assert not path.exists() # Should not create actual directory
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Test SSNExtrema class.
|
|
3
|
+
|
|
4
|
+
This module tests the SSNExtrema class from solar_activity.sunspot_number.sidc:
|
|
5
|
+
- CSV parsing and data loading
|
|
6
|
+
- Error handling for invalid arguments
|
|
7
|
+
- Data format validation
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
import pandas as pd
|
|
12
|
+
import numpy as np
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from unittest.mock import Mock, patch, mock_open
|
|
15
|
+
|
|
16
|
+
from solarwindpy.solar_activity.sunspot_number.sidc import SSNExtrema
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TestSSNExtrema:
|
|
20
|
+
"""Test the SSNExtrema class for solar cycle extrema data."""
|
|
21
|
+
|
|
22
|
+
@pytest.fixture
|
|
23
|
+
def sample_ssn_extrema_csv(self):
|
|
24
|
+
"""Sample SSN extrema CSV content for testing."""
|
|
25
|
+
# Simulate the format of ssn_extrema.csv with header rows and data
|
|
26
|
+
csv_content = (
|
|
27
|
+
"""# Solar Cycle Extrema Data
|
|
28
|
+
# Source: SIDC - Royal Observatory of Belgium
|
|
29
|
+
# Data format: Cycle Number, Minimum Date, Maximum Date
|
|
30
|
+
#
|
|
31
|
+
# Additional header lines (total 45 lines to skip)
|
|
32
|
+
"""
|
|
33
|
+
+ "\n" * 40
|
|
34
|
+
+ """Number,Min,Max
|
|
35
|
+
20,1964-10-01,1968-11-01
|
|
36
|
+
21,1976-03-01,1979-12-01
|
|
37
|
+
22,1986-09-01,1989-07-01
|
|
38
|
+
23,1996-05-01,2000-03-01
|
|
39
|
+
24,2008-12-01,2014-04-01
|
|
40
|
+
25,2019-12-01,2025-07-01"""
|
|
41
|
+
)
|
|
42
|
+
return csv_content
|
|
43
|
+
|
|
44
|
+
def test_initialization_no_args(self):
|
|
45
|
+
"""Test SSNExtrema initialization with no arguments."""
|
|
46
|
+
# This should work without raising errors
|
|
47
|
+
with (
|
|
48
|
+
patch("pandas.read_csv") as mock_read_csv,
|
|
49
|
+
patch("pathlib.Path.exists", return_value=True),
|
|
50
|
+
):
|
|
51
|
+
|
|
52
|
+
# Mock the CSV data
|
|
53
|
+
mock_data = pd.DataFrame(
|
|
54
|
+
{
|
|
55
|
+
"Min": ["1964-10-01", "1976-03-01", "1986-09-01"],
|
|
56
|
+
"Max": ["1968-11-01", "1979-12-01", "1989-07-01"],
|
|
57
|
+
},
|
|
58
|
+
index=[20, 21, 22],
|
|
59
|
+
)
|
|
60
|
+
mock_read_csv.return_value = mock_data
|
|
61
|
+
|
|
62
|
+
extrema = SSNExtrema()
|
|
63
|
+
|
|
64
|
+
# Verify that the load_or_set_data method was called implicitly
|
|
65
|
+
assert hasattr(extrema, "_data")
|
|
66
|
+
|
|
67
|
+
def test_load_or_set_data_empty_args(self, sample_ssn_extrema_csv):
|
|
68
|
+
"""Test load_or_set_data with empty args and kwargs."""
|
|
69
|
+
extrema = SSNExtrema.__new__(SSNExtrema)
|
|
70
|
+
|
|
71
|
+
# Mock file reading
|
|
72
|
+
with (
|
|
73
|
+
patch("pandas.read_csv") as mock_read_csv,
|
|
74
|
+
patch("pathlib.Path.exists", return_value=True),
|
|
75
|
+
):
|
|
76
|
+
|
|
77
|
+
# Create mock DataFrame that simulates the CSV structure
|
|
78
|
+
raw_data = pd.DataFrame(
|
|
79
|
+
{
|
|
80
|
+
"Min": ["1964-10-01", "1976-03-01", "1986-09-01"],
|
|
81
|
+
"Max": ["1968-11-01", "1979-12-01", "1989-07-01"],
|
|
82
|
+
},
|
|
83
|
+
index=[20, 21, 22],
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Mock the stack/unstack operations
|
|
87
|
+
stacked = pd.Series(
|
|
88
|
+
[
|
|
89
|
+
"1964-10-01",
|
|
90
|
+
"1968-11-01",
|
|
91
|
+
"1976-03-01",
|
|
92
|
+
"1979-12-01",
|
|
93
|
+
"1986-09-01",
|
|
94
|
+
"1989-07-01",
|
|
95
|
+
]
|
|
96
|
+
)
|
|
97
|
+
unstacked = pd.DataFrame(
|
|
98
|
+
{
|
|
99
|
+
"Min": [
|
|
100
|
+
pd.Timestamp("1964-10-01"),
|
|
101
|
+
pd.Timestamp("1976-03-01"),
|
|
102
|
+
pd.Timestamp("1986-09-01"),
|
|
103
|
+
],
|
|
104
|
+
"Max": [
|
|
105
|
+
pd.Timestamp("1968-11-01"),
|
|
106
|
+
pd.Timestamp("1979-12-01"),
|
|
107
|
+
pd.Timestamp("1989-07-01"),
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
index=[20, 21, 22],
|
|
111
|
+
)
|
|
112
|
+
unstacked.columns.names = ["kind"]
|
|
113
|
+
|
|
114
|
+
mock_read_csv.return_value = raw_data
|
|
115
|
+
|
|
116
|
+
with (
|
|
117
|
+
patch.object(pd.DataFrame, "stack", return_value=stacked),
|
|
118
|
+
patch("pandas.to_datetime", return_value=stacked),
|
|
119
|
+
patch.object(pd.Series, "unstack", return_value=unstacked),
|
|
120
|
+
):
|
|
121
|
+
|
|
122
|
+
extrema.load_or_set_data()
|
|
123
|
+
|
|
124
|
+
# Verify CSV was read with correct parameters
|
|
125
|
+
mock_read_csv.assert_called_once()
|
|
126
|
+
args, kwargs = mock_read_csv.call_args
|
|
127
|
+
assert kwargs["header"] == 0
|
|
128
|
+
assert kwargs["skiprows"] == 45
|
|
129
|
+
assert kwargs["index_col"] == 0
|
|
130
|
+
|
|
131
|
+
# Verify data was set
|
|
132
|
+
assert hasattr(extrema, "_data")
|
|
133
|
+
assert extrema._data.columns.names == ["kind"]
|
|
134
|
+
|
|
135
|
+
def test_load_or_set_data_with_args_raises_error(self):
|
|
136
|
+
"""Test that passing args/kwargs raises ValueError."""
|
|
137
|
+
extrema = SSNExtrema.__new__(SSNExtrema)
|
|
138
|
+
|
|
139
|
+
# Should raise ValueError when passing arguments
|
|
140
|
+
with pytest.raises(
|
|
141
|
+
ValueError, match="SSNExtrema expects empty args and kwargs"
|
|
142
|
+
):
|
|
143
|
+
extrema.load_or_set_data("some_arg")
|
|
144
|
+
|
|
145
|
+
def test_load_or_set_data_with_kwargs_raises_error(self):
|
|
146
|
+
"""Test that passing kwargs raises ValueError."""
|
|
147
|
+
extrema = SSNExtrema.__new__(SSNExtrema)
|
|
148
|
+
|
|
149
|
+
# Should raise ValueError when passing keyword arguments
|
|
150
|
+
with pytest.raises(
|
|
151
|
+
ValueError, match="SSNExtrema expects empty args and kwargs"
|
|
152
|
+
):
|
|
153
|
+
extrema.load_or_set_data(some_kwarg="value")
|
|
154
|
+
|
|
155
|
+
def test_load_or_set_data_with_both_args_and_kwargs_raises_error(self):
|
|
156
|
+
"""Test that passing both args and kwargs raises ValueError."""
|
|
157
|
+
extrema = SSNExtrema.__new__(SSNExtrema)
|
|
158
|
+
|
|
159
|
+
# Should raise ValueError when passing both
|
|
160
|
+
with pytest.raises(
|
|
161
|
+
ValueError, match="SSNExtrema expects empty args and kwargs"
|
|
162
|
+
):
|
|
163
|
+
extrema.load_or_set_data("arg", kwarg="value")
|
|
164
|
+
|
|
165
|
+
def test_inheritance_from_indicator_extrema(self):
|
|
166
|
+
"""Test that SSNExtrema inherits from IndicatorExtrema."""
|
|
167
|
+
from solarwindpy.solar_activity.base import IndicatorExtrema
|
|
168
|
+
|
|
169
|
+
with (
|
|
170
|
+
patch("pandas.read_csv") as mock_read_csv,
|
|
171
|
+
patch("pathlib.Path.exists", return_value=True),
|
|
172
|
+
):
|
|
173
|
+
|
|
174
|
+
# Mock the CSV data
|
|
175
|
+
mock_data = pd.DataFrame(
|
|
176
|
+
{"Min": ["1964-10-01"], "Max": ["1968-11-01"]}, index=[20]
|
|
177
|
+
)
|
|
178
|
+
mock_read_csv.return_value = mock_data
|
|
179
|
+
|
|
180
|
+
extrema = SSNExtrema()
|
|
181
|
+
assert isinstance(extrema, IndicatorExtrema)
|
|
182
|
+
|
|
183
|
+
def test_csv_file_path_resolution(self):
|
|
184
|
+
"""Test that the CSV file path is resolved correctly."""
|
|
185
|
+
extrema = SSNExtrema.__new__(SSNExtrema)
|
|
186
|
+
|
|
187
|
+
with (
|
|
188
|
+
patch("pandas.read_csv") as mock_read_csv,
|
|
189
|
+
patch("pathlib.Path.exists", return_value=True),
|
|
190
|
+
):
|
|
191
|
+
|
|
192
|
+
mock_data = pd.DataFrame(
|
|
193
|
+
{"Min": ["1964-10-01"], "Max": ["1968-11-01"]}, index=[20]
|
|
194
|
+
)
|
|
195
|
+
mock_read_csv.return_value = mock_data
|
|
196
|
+
|
|
197
|
+
extrema.load_or_set_data()
|
|
198
|
+
|
|
199
|
+
# Check that read_csv was called with the expected path
|
|
200
|
+
args, kwargs = mock_read_csv.call_args
|
|
201
|
+
called_path = args[0]
|
|
202
|
+
|
|
203
|
+
# The path should end with ssn_extrema.csv
|
|
204
|
+
assert str(called_path).endswith("ssn_extrema.csv")
|
|
205
|
+
assert "sunspot_number" in str(called_path)
|
|
206
|
+
|
|
207
|
+
def test_data_format_after_loading(self):
|
|
208
|
+
"""Test that data has correct format after loading."""
|
|
209
|
+
with (
|
|
210
|
+
patch("pandas.read_csv") as mock_read_csv,
|
|
211
|
+
patch("pathlib.Path.exists", return_value=True),
|
|
212
|
+
):
|
|
213
|
+
|
|
214
|
+
# Mock the CSV reading and processing
|
|
215
|
+
raw_data = pd.DataFrame(
|
|
216
|
+
{
|
|
217
|
+
"Min": ["2008-12-01", "2019-12-01"],
|
|
218
|
+
"Max": ["2014-04-01", "2025-07-01"],
|
|
219
|
+
},
|
|
220
|
+
index=[24, 25],
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
processed_data = pd.DataFrame(
|
|
224
|
+
{
|
|
225
|
+
"Min": [pd.Timestamp("2008-12-01"), pd.Timestamp("2019-12-01")],
|
|
226
|
+
"Max": [pd.Timestamp("2014-04-01"), pd.Timestamp("2025-07-01")],
|
|
227
|
+
},
|
|
228
|
+
index=[24, 25],
|
|
229
|
+
)
|
|
230
|
+
processed_data.columns.names = ["kind"]
|
|
231
|
+
|
|
232
|
+
mock_read_csv.return_value = raw_data
|
|
233
|
+
|
|
234
|
+
# Mock the datetime conversion process
|
|
235
|
+
with patch("solarwindpy.solar_activity.sunspot_number.sidc.pd.to_datetime") as mock_to_datetime:
|
|
236
|
+
# Mock stack operation
|
|
237
|
+
stacked_data = pd.Series(
|
|
238
|
+
["2008-12-01", "2014-04-01", "2019-12-01", "2025-07-01"]
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# Mock to_datetime to return datetime series
|
|
242
|
+
datetime_series = pd.Series(
|
|
243
|
+
[
|
|
244
|
+
pd.Timestamp("2008-12-01"),
|
|
245
|
+
pd.Timestamp("2014-04-01"),
|
|
246
|
+
pd.Timestamp("2019-12-01"),
|
|
247
|
+
pd.Timestamp("2025-07-01"),
|
|
248
|
+
]
|
|
249
|
+
)
|
|
250
|
+
mock_to_datetime.return_value = datetime_series
|
|
251
|
+
|
|
252
|
+
with (
|
|
253
|
+
patch.object(pd.DataFrame, "stack", return_value=stacked_data),
|
|
254
|
+
patch.object(pd.Series, "unstack", return_value=processed_data),
|
|
255
|
+
patch.object(SSNExtrema, "calculate_intervals"), # Skip interval calculation that uses "today"
|
|
256
|
+
):
|
|
257
|
+
|
|
258
|
+
extrema = SSNExtrema()
|
|
259
|
+
|
|
260
|
+
# Verify data structure
|
|
261
|
+
assert hasattr(extrema, "_data")
|
|
262
|
+
assert extrema._data.columns.names == ["kind"]
|
|
263
|
+
assert "Min" in extrema._data.columns
|
|
264
|
+
assert "Max" in extrema._data.columns
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class TestSSNExtremaEdgeCases:
|
|
268
|
+
"""Test edge cases and error conditions for SSNExtrema."""
|
|
269
|
+
|
|
270
|
+
def test_file_not_found_handling(self):
|
|
271
|
+
"""Test behavior when CSV file doesn't exist."""
|
|
272
|
+
with (
|
|
273
|
+
patch("pathlib.Path.exists", return_value=False),
|
|
274
|
+
patch("pandas.read_csv") as mock_read_csv,
|
|
275
|
+
):
|
|
276
|
+
|
|
277
|
+
# read_csv should still be called even if file check fails
|
|
278
|
+
# (pandas will handle the FileNotFoundError)
|
|
279
|
+
mock_read_csv.side_effect = FileNotFoundError("File not found")
|
|
280
|
+
|
|
281
|
+
with pytest.raises(FileNotFoundError):
|
|
282
|
+
SSNExtrema()
|
|
283
|
+
|
|
284
|
+
def test_malformed_csv_handling(self):
|
|
285
|
+
"""Test behavior with malformed CSV data."""
|
|
286
|
+
with (
|
|
287
|
+
patch("pandas.read_csv") as mock_read_csv,
|
|
288
|
+
patch("pathlib.Path.exists", return_value=True),
|
|
289
|
+
):
|
|
290
|
+
|
|
291
|
+
# Simulate malformed CSV that causes parsing error
|
|
292
|
+
mock_read_csv.side_effect = pd.errors.ParserError("Malformed CSV")
|
|
293
|
+
|
|
294
|
+
with pytest.raises(pd.errors.ParserError):
|
|
295
|
+
SSNExtrema()
|
|
296
|
+
|
|
297
|
+
def test_empty_csv_handling(self):
|
|
298
|
+
"""Test behavior with empty CSV data."""
|
|
299
|
+
with (
|
|
300
|
+
patch("pandas.read_csv") as mock_read_csv,
|
|
301
|
+
patch("pathlib.Path.exists", return_value=True),
|
|
302
|
+
):
|
|
303
|
+
|
|
304
|
+
# Empty DataFrame
|
|
305
|
+
empty_data = pd.DataFrame()
|
|
306
|
+
mock_read_csv.return_value = empty_data
|
|
307
|
+
|
|
308
|
+
extrema = SSNExtrema.__new__(SSNExtrema)
|
|
309
|
+
|
|
310
|
+
# Should handle empty data gracefully (might raise error in stack/unstack)
|
|
311
|
+
try:
|
|
312
|
+
extrema.load_or_set_data()
|
|
313
|
+
except (ValueError, IndexError):
|
|
314
|
+
# These are acceptable errors for empty data
|
|
315
|
+
pass
|
|
316
|
+
|
|
317
|
+
def test_invalid_date_format_handling(self):
|
|
318
|
+
"""Test behavior with invalid date formats in CSV."""
|
|
319
|
+
with (
|
|
320
|
+
patch("pandas.read_csv") as mock_read_csv,
|
|
321
|
+
patch("pathlib.Path.exists", return_value=True),
|
|
322
|
+
):
|
|
323
|
+
|
|
324
|
+
# Data with invalid date formats
|
|
325
|
+
invalid_data = pd.DataFrame(
|
|
326
|
+
{
|
|
327
|
+
"Min": ["invalid-date", "1976-03-01"],
|
|
328
|
+
"Max": ["1968-11-01", "another-invalid-date"],
|
|
329
|
+
},
|
|
330
|
+
index=[20, 21],
|
|
331
|
+
)
|
|
332
|
+
mock_read_csv.return_value = invalid_data
|
|
333
|
+
|
|
334
|
+
extrema = SSNExtrema.__new__(SSNExtrema)
|
|
335
|
+
|
|
336
|
+
# pandas.to_datetime should handle invalid dates (might raise error or coerce)
|
|
337
|
+
with patch("solarwindpy.solar_activity.sunspot_number.sidc.pd.to_datetime") as mock_to_datetime:
|
|
338
|
+
mock_to_datetime.side_effect = ValueError("Invalid date format")
|
|
339
|
+
|
|
340
|
+
with pytest.raises(ValueError):
|
|
341
|
+
extrema.load_or_set_data()
|
|
342
|
+
|
|
343
|
+
def test_class_name_in_error_message(self):
|
|
344
|
+
"""Test that class name appears correctly in error messages."""
|
|
345
|
+
extrema = SSNExtrema.__new__(SSNExtrema)
|
|
346
|
+
|
|
347
|
+
# Test that the error message contains the correct class name
|
|
348
|
+
with pytest.raises(ValueError) as exc_info:
|
|
349
|
+
extrema.load_or_set_data("arg")
|
|
350
|
+
|
|
351
|
+
assert "SSNExtrema" in str(exc_info.value)
|
|
352
|
+
|
|
353
|
+
def test_method_signature_validation(self):
|
|
354
|
+
"""Test that method accepts various argument patterns for validation."""
|
|
355
|
+
extrema = SSNExtrema.__new__(SSNExtrema)
|
|
356
|
+
|
|
357
|
+
# Test different argument patterns that should all raise errors
|
|
358
|
+
error_cases = [
|
|
359
|
+
(["arg1"], {}),
|
|
360
|
+
([], {"key": "value"}),
|
|
361
|
+
(["arg1", "arg2"], {}),
|
|
362
|
+
(["arg"], {"key": "value"}),
|
|
363
|
+
([], {"key1": "value1", "key2": "value2"}),
|
|
364
|
+
]
|
|
365
|
+
|
|
366
|
+
for args, kwargs in error_cases:
|
|
367
|
+
with pytest.raises(
|
|
368
|
+
ValueError, match="SSNExtrema expects empty args and kwargs"
|
|
369
|
+
):
|
|
370
|
+
extrema.load_or_set_data(*args, **kwargs)
|
|
371
|
+
|
|
372
|
+
def test_successful_initialization_creates_expected_attributes(self):
|
|
373
|
+
"""Test that successful initialization creates expected attributes."""
|
|
374
|
+
with (
|
|
375
|
+
patch("pandas.read_csv") as mock_read_csv,
|
|
376
|
+
patch("pathlib.Path.exists", return_value=True),
|
|
377
|
+
):
|
|
378
|
+
|
|
379
|
+
# Mock successful CSV loading
|
|
380
|
+
mock_data = pd.DataFrame(
|
|
381
|
+
{"Min": ["2008-12-01"], "Max": ["2014-04-01"]}, index=[24]
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
processed_data = pd.DataFrame(
|
|
385
|
+
{
|
|
386
|
+
"Min": [pd.Timestamp("2008-12-01")],
|
|
387
|
+
"Max": [pd.Timestamp("2014-04-01")],
|
|
388
|
+
},
|
|
389
|
+
index=[24],
|
|
390
|
+
)
|
|
391
|
+
processed_data.columns.names = ["kind"]
|
|
392
|
+
|
|
393
|
+
mock_read_csv.return_value = mock_data
|
|
394
|
+
|
|
395
|
+
with (
|
|
396
|
+
patch("pandas.to_datetime"),
|
|
397
|
+
patch.object(pd.DataFrame, "stack"),
|
|
398
|
+
patch.object(pd.Series, "unstack", return_value=processed_data),
|
|
399
|
+
):
|
|
400
|
+
|
|
401
|
+
extrema = SSNExtrema()
|
|
402
|
+
|
|
403
|
+
# Check that the object has expected attributes from parent class
|
|
404
|
+
assert hasattr(extrema, "_data")
|
|
405
|
+
# IndicatorExtrema parent should provide additional attributes
|
|
406
|
+
assert hasattr(extrema, "data") # Property from parent
|