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,775 @@
|
|
|
1
|
+
"""Test fixtures and utilities for plotting functionality.
|
|
2
|
+
|
|
3
|
+
Comprehensive test infrastructure, fixtures, and utility functions for the plotting test
|
|
4
|
+
suite.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import matplotlib
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
import tempfile
|
|
14
|
+
import warnings
|
|
15
|
+
from unittest.mock import patch, MagicMock
|
|
16
|
+
import pickle
|
|
17
|
+
|
|
18
|
+
# Configure matplotlib for testing
|
|
19
|
+
matplotlib.use("Agg")
|
|
20
|
+
plt.ioff()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ============================================================================
|
|
24
|
+
# Core Fixtures
|
|
25
|
+
# ============================================================================
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.fixture(scope="session")
|
|
29
|
+
def matplotlib_backend():
|
|
30
|
+
"""Ensure consistent matplotlib backend for all tests."""
|
|
31
|
+
original_backend = matplotlib.get_backend()
|
|
32
|
+
matplotlib.use("Agg")
|
|
33
|
+
yield "Agg"
|
|
34
|
+
matplotlib.use(original_backend)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.fixture(scope="function")
|
|
38
|
+
def clean_matplotlib():
|
|
39
|
+
"""Clean matplotlib state before and after each test."""
|
|
40
|
+
plt.close("all")
|
|
41
|
+
plt.rcdefaults() # Reset to default rc parameters
|
|
42
|
+
yield
|
|
43
|
+
plt.close("all")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@pytest.fixture(scope="function")
|
|
47
|
+
def suppress_warnings():
|
|
48
|
+
"""Suppress common matplotlib and numpy warnings during tests."""
|
|
49
|
+
with warnings.catch_warnings():
|
|
50
|
+
warnings.simplefilter("ignore", UserWarning)
|
|
51
|
+
warnings.simplefilter("ignore", RuntimeWarning)
|
|
52
|
+
warnings.simplefilter("ignore", FutureWarning)
|
|
53
|
+
yield
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# ============================================================================
|
|
57
|
+
# Data Fixtures
|
|
58
|
+
# ============================================================================
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@pytest.fixture(scope="session")
|
|
62
|
+
def synthetic_time_series():
|
|
63
|
+
"""Generate synthetic time series data for testing."""
|
|
64
|
+
dates = pd.date_range("2023-01-01", periods=1000, freq="1h")
|
|
65
|
+
np.random.seed(42)
|
|
66
|
+
|
|
67
|
+
# Solar wind parameters with realistic variations
|
|
68
|
+
n = len(dates)
|
|
69
|
+
time_index = np.arange(n)
|
|
70
|
+
|
|
71
|
+
data = {
|
|
72
|
+
"density": 10 + 2 * np.sin(time_index / 100) + np.random.normal(0, 0.5, n),
|
|
73
|
+
"velocity": 400 + 50 * np.sin(time_index / 150) + np.random.normal(0, 10, n),
|
|
74
|
+
"temperature": 1e5
|
|
75
|
+
+ 2e4 * np.sin(time_index / 80)
|
|
76
|
+
+ np.random.normal(0, 5e3, n),
|
|
77
|
+
"b_field": 5 + np.sin(time_index / 120) + np.random.normal(0, 0.2, n),
|
|
78
|
+
"pressure": 1.5 + 0.3 * np.sin(time_index / 90) + np.random.normal(0, 0.1, n),
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return pd.DataFrame(data, index=dates)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@pytest.fixture(scope="session")
|
|
85
|
+
def synthetic_plasma_data():
|
|
86
|
+
"""Generate synthetic plasma data for correlation analysis."""
|
|
87
|
+
np.random.seed(42)
|
|
88
|
+
n_points = 500
|
|
89
|
+
|
|
90
|
+
# Correlated plasma parameters
|
|
91
|
+
density = np.random.lognormal(mean=2.3, sigma=0.5, size=n_points) # ~10 cm^-3
|
|
92
|
+
velocity = (
|
|
93
|
+
400 + 100 * (1 / density) + np.random.normal(0, 20, n_points)
|
|
94
|
+
) # Anti-corr with density
|
|
95
|
+
temperature = 1e5 * (velocity / 400) ** 1.5 + np.random.normal(
|
|
96
|
+
0, 2e4, n_points
|
|
97
|
+
) # Corr with velocity
|
|
98
|
+
b_field = 5 * np.sqrt(density) + np.random.normal(
|
|
99
|
+
0, 1, n_points
|
|
100
|
+
) # Corr with density
|
|
101
|
+
|
|
102
|
+
return pd.DataFrame(
|
|
103
|
+
{
|
|
104
|
+
"density": density,
|
|
105
|
+
"velocity": velocity,
|
|
106
|
+
"temperature": temperature,
|
|
107
|
+
"b_field": b_field,
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@pytest.fixture(scope="function")
|
|
113
|
+
def small_dataset():
|
|
114
|
+
"""Small dataset for quick tests."""
|
|
115
|
+
np.random.seed(42)
|
|
116
|
+
x = np.linspace(0, 10, 50)
|
|
117
|
+
y = np.sin(x) + 0.1 * np.random.randn(50)
|
|
118
|
+
return x, y
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@pytest.fixture(scope="function")
|
|
122
|
+
def medium_dataset():
|
|
123
|
+
"""Medium dataset for standard tests."""
|
|
124
|
+
np.random.seed(42)
|
|
125
|
+
x = np.linspace(0, 20, 1000)
|
|
126
|
+
y = np.sin(x) * np.exp(-x / 10) + 0.1 * np.random.randn(1000)
|
|
127
|
+
return x, y
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@pytest.fixture(scope="function")
|
|
131
|
+
def large_dataset():
|
|
132
|
+
"""Large dataset for performance tests."""
|
|
133
|
+
np.random.seed(42)
|
|
134
|
+
x = np.linspace(0, 100, 100000)
|
|
135
|
+
y = np.sin(x / 10) + 0.1 * np.random.randn(100000)
|
|
136
|
+
return x, y
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@pytest.fixture(scope="function")
|
|
140
|
+
def scatter_data():
|
|
141
|
+
"""Scatter plot data with color mapping."""
|
|
142
|
+
np.random.seed(42)
|
|
143
|
+
n = 200
|
|
144
|
+
x = np.random.randn(n)
|
|
145
|
+
y = np.random.randn(n)
|
|
146
|
+
colors = np.random.rand(n)
|
|
147
|
+
sizes = 20 + 50 * np.random.rand(n)
|
|
148
|
+
return x, y, colors, sizes
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@pytest.fixture(scope="function")
|
|
152
|
+
def histogram_data():
|
|
153
|
+
"""Data for histogram testing."""
|
|
154
|
+
np.random.seed(42)
|
|
155
|
+
return {
|
|
156
|
+
"normal": np.random.normal(0, 1, 1000),
|
|
157
|
+
"exponential": np.random.exponential(2, 1000),
|
|
158
|
+
"uniform": np.random.uniform(-3, 3, 1000),
|
|
159
|
+
"mixed": np.concatenate(
|
|
160
|
+
[np.random.normal(-2, 0.5, 300), np.random.normal(2, 0.8, 700)]
|
|
161
|
+
),
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@pytest.fixture(scope="function")
|
|
166
|
+
def contour_data():
|
|
167
|
+
"""2D data for contour/surface plots."""
|
|
168
|
+
x = np.linspace(-5, 5, 50)
|
|
169
|
+
y = np.linspace(-5, 5, 50)
|
|
170
|
+
X, Y = np.meshgrid(x, y)
|
|
171
|
+
Z = np.sin(np.sqrt(X**2 + Y**2)) * np.exp(-0.1 * (X**2 + Y**2))
|
|
172
|
+
return X, Y, Z
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# ============================================================================
|
|
176
|
+
# Figure and Axes Fixtures
|
|
177
|
+
# ============================================================================
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@pytest.fixture(scope="function")
|
|
181
|
+
def single_axis(clean_matplotlib):
|
|
182
|
+
"""Single axis figure for basic plots."""
|
|
183
|
+
fig, ax = plt.subplots(figsize=(8, 6))
|
|
184
|
+
yield fig, ax
|
|
185
|
+
plt.close(fig)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@pytest.fixture(scope="function")
|
|
189
|
+
def multi_axis(clean_matplotlib):
|
|
190
|
+
"""Multi-axis figure for subplot tests."""
|
|
191
|
+
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
|
|
192
|
+
yield fig, axes
|
|
193
|
+
plt.close(fig)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@pytest.fixture(scope="function")
|
|
197
|
+
def time_series_axes(clean_matplotlib):
|
|
198
|
+
"""Specialized axes for time series plots."""
|
|
199
|
+
fig, axes = plt.subplots(3, 1, figsize=(12, 10), sharex=True)
|
|
200
|
+
yield fig, axes
|
|
201
|
+
plt.close(fig)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@pytest.fixture(scope="function")
|
|
205
|
+
def correlation_axes(clean_matplotlib):
|
|
206
|
+
"""Correlation plot axes configuration."""
|
|
207
|
+
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
|
|
208
|
+
yield fig, axes
|
|
209
|
+
plt.close(fig)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# ============================================================================
|
|
213
|
+
# Temporary File Fixtures
|
|
214
|
+
# ============================================================================
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@pytest.fixture(scope="function")
|
|
218
|
+
def temp_dir():
|
|
219
|
+
"""Temporary directory for file operations."""
|
|
220
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
221
|
+
yield Path(tmpdir)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@pytest.fixture(scope="function")
|
|
225
|
+
def temp_plot_file(temp_dir):
|
|
226
|
+
"""Temporary file for plot saving."""
|
|
227
|
+
return temp_dir / "test_plot.png"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@pytest.fixture(scope="function")
|
|
231
|
+
def temp_data_file(temp_dir):
|
|
232
|
+
"""Temporary file for data saving."""
|
|
233
|
+
return temp_dir / "test_data.csv"
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
# ============================================================================
|
|
237
|
+
# Mock Fixtures
|
|
238
|
+
# ============================================================================
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@pytest.fixture(scope="function")
|
|
242
|
+
def mock_figure():
|
|
243
|
+
"""Mock matplotlib figure for unit testing."""
|
|
244
|
+
with patch("matplotlib.pyplot.figure") as mock_fig:
|
|
245
|
+
mock_instance = MagicMock()
|
|
246
|
+
mock_fig.return_value = mock_instance
|
|
247
|
+
yield mock_instance
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@pytest.fixture(scope="function")
|
|
251
|
+
def mock_axes():
|
|
252
|
+
"""Mock matplotlib axes for unit testing."""
|
|
253
|
+
mock_ax = MagicMock()
|
|
254
|
+
mock_ax.plot.return_value = [MagicMock()] # Line2D objects
|
|
255
|
+
mock_ax.scatter.return_value = MagicMock() # PathCollection
|
|
256
|
+
mock_ax.hist.return_value = (
|
|
257
|
+
np.array([1, 2, 3]),
|
|
258
|
+
np.array([0, 1, 2, 3]),
|
|
259
|
+
[MagicMock()],
|
|
260
|
+
)
|
|
261
|
+
return mock_ax
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
# ============================================================================
|
|
265
|
+
# Utility Classes and Functions
|
|
266
|
+
# ============================================================================
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class PlotTestHelper:
|
|
270
|
+
"""Helper class for common plotting test operations."""
|
|
271
|
+
|
|
272
|
+
@staticmethod
|
|
273
|
+
def assert_plot_elements(
|
|
274
|
+
ax,
|
|
275
|
+
expected_lines=None,
|
|
276
|
+
expected_collections=None,
|
|
277
|
+
expected_patches=None,
|
|
278
|
+
has_legend=None,
|
|
279
|
+
has_grid=None,
|
|
280
|
+
):
|
|
281
|
+
"""Assert expected plot elements are present."""
|
|
282
|
+
if expected_lines is not None:
|
|
283
|
+
assert (
|
|
284
|
+
len(ax.lines) == expected_lines
|
|
285
|
+
), f"Expected {expected_lines} lines, got {len(ax.lines)}"
|
|
286
|
+
|
|
287
|
+
if expected_collections is not None:
|
|
288
|
+
assert (
|
|
289
|
+
len(ax.collections) == expected_collections
|
|
290
|
+
), f"Expected {expected_collections} collections, got {len(ax.collections)}"
|
|
291
|
+
|
|
292
|
+
if expected_patches is not None:
|
|
293
|
+
assert (
|
|
294
|
+
len(ax.patches) == expected_patches
|
|
295
|
+
), f"Expected {expected_patches} patches, got {len(ax.patches)}"
|
|
296
|
+
|
|
297
|
+
if has_legend is not None:
|
|
298
|
+
legend_exists = ax.legend_ is not None
|
|
299
|
+
assert (
|
|
300
|
+
legend_exists == has_legend
|
|
301
|
+
), f"Legend existence mismatch: expected {has_legend}, got {legend_exists}"
|
|
302
|
+
|
|
303
|
+
if has_grid is not None:
|
|
304
|
+
# Check if grid is visible
|
|
305
|
+
grid_on = (
|
|
306
|
+
ax.xaxis.get_gridlines()[0].get_visible()
|
|
307
|
+
if ax.xaxis.get_gridlines()
|
|
308
|
+
else False
|
|
309
|
+
)
|
|
310
|
+
assert (
|
|
311
|
+
grid_on == has_grid
|
|
312
|
+
), f"Grid visibility mismatch: expected {has_grid}, got {grid_on}"
|
|
313
|
+
|
|
314
|
+
@staticmethod
|
|
315
|
+
def assert_axes_labels(ax, xlabel=None, ylabel=None, title=None):
|
|
316
|
+
"""Assert axes labels are set correctly."""
|
|
317
|
+
if xlabel is not None:
|
|
318
|
+
assert (
|
|
319
|
+
ax.get_xlabel() == xlabel
|
|
320
|
+
), f"X-label mismatch: expected '{xlabel}', got '{ax.get_xlabel()}'"
|
|
321
|
+
|
|
322
|
+
if ylabel is not None:
|
|
323
|
+
assert (
|
|
324
|
+
ax.get_ylabel() == ylabel
|
|
325
|
+
), f"Y-label mismatch: expected '{ylabel}', got '{ax.get_ylabel()}'"
|
|
326
|
+
|
|
327
|
+
if title is not None:
|
|
328
|
+
assert (
|
|
329
|
+
ax.get_title() == title
|
|
330
|
+
), f"Title mismatch: expected '{title}', got '{ax.get_title()}'"
|
|
331
|
+
|
|
332
|
+
@staticmethod
|
|
333
|
+
def assert_data_ranges(ax, xlim=None, ylim=None, tolerance=1e-10):
|
|
334
|
+
"""Assert data ranges are within expected limits."""
|
|
335
|
+
if xlim is not None:
|
|
336
|
+
actual_xlim = ax.get_xlim()
|
|
337
|
+
assert (
|
|
338
|
+
abs(actual_xlim[0] - xlim[0]) < tolerance
|
|
339
|
+
and abs(actual_xlim[1] - xlim[1]) < tolerance
|
|
340
|
+
), f"X-limits mismatch: expected {xlim}, got {actual_xlim}"
|
|
341
|
+
|
|
342
|
+
if ylim is not None:
|
|
343
|
+
actual_ylim = ax.get_ylim()
|
|
344
|
+
assert (
|
|
345
|
+
abs(actual_ylim[0] - ylim[0]) < tolerance
|
|
346
|
+
and abs(actual_ylim[1] - ylim[1]) < tolerance
|
|
347
|
+
), f"Y-limits mismatch: expected {ylim}, got {actual_ylim}"
|
|
348
|
+
|
|
349
|
+
@staticmethod
|
|
350
|
+
def save_and_verify_plot(fig, filepath, formats=["png"], dpi=100):
|
|
351
|
+
"""Save plot in multiple formats and verify files."""
|
|
352
|
+
saved_files = []
|
|
353
|
+
|
|
354
|
+
for fmt in formats:
|
|
355
|
+
if isinstance(filepath, Path):
|
|
356
|
+
file_path = filepath.with_suffix(f".{fmt}")
|
|
357
|
+
else:
|
|
358
|
+
file_path = Path(str(filepath)).with_suffix(f".{fmt}")
|
|
359
|
+
|
|
360
|
+
fig.savefig(file_path, format=fmt, dpi=dpi, bbox_inches="tight")
|
|
361
|
+
|
|
362
|
+
# Verify file was created and has content
|
|
363
|
+
assert file_path.exists(), f"File {file_path} was not created"
|
|
364
|
+
assert file_path.stat().st_size > 0, f"File {file_path} is empty"
|
|
365
|
+
|
|
366
|
+
saved_files.append(file_path)
|
|
367
|
+
|
|
368
|
+
return saved_files
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
@pytest.fixture(scope="function")
|
|
372
|
+
def plot_helper():
|
|
373
|
+
"""Provide PlotTestHelper instance."""
|
|
374
|
+
return PlotTestHelper()
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
# ============================================================================
|
|
378
|
+
# Test Data Generators
|
|
379
|
+
# ============================================================================
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def generate_solar_wind_event(duration_hours=24, cadence_minutes=1, event_type="shock"):
|
|
383
|
+
"""Generate synthetic solar wind event data."""
|
|
384
|
+
n_points = int(duration_hours * 60 / cadence_minutes)
|
|
385
|
+
times = pd.date_range("2023-01-01", periods=n_points, freq=f"{cadence_minutes}min")
|
|
386
|
+
|
|
387
|
+
if event_type == "shock":
|
|
388
|
+
# Sudden jump in parameters
|
|
389
|
+
shock_time = n_points // 2
|
|
390
|
+
|
|
391
|
+
density = np.ones(n_points) * 5
|
|
392
|
+
density[shock_time:] = 20 # Density jump
|
|
393
|
+
density += np.random.normal(0, 1, n_points)
|
|
394
|
+
|
|
395
|
+
velocity = np.ones(n_points) * 350
|
|
396
|
+
velocity[shock_time:] = 600 # Velocity jump
|
|
397
|
+
velocity += np.random.normal(0, 15, n_points)
|
|
398
|
+
|
|
399
|
+
temperature = np.ones(n_points) * 5e4
|
|
400
|
+
temperature[shock_time:] = 2e5 # Temperature jump
|
|
401
|
+
temperature += np.random.normal(0, 1e4, n_points)
|
|
402
|
+
|
|
403
|
+
elif event_type == "cme":
|
|
404
|
+
# Gradual rotation in magnetic field
|
|
405
|
+
b_x = 5 * np.sin(np.linspace(0, 4 * np.pi, n_points))
|
|
406
|
+
b_y = 5 * np.cos(np.linspace(0, 4 * np.pi, n_points))
|
|
407
|
+
b_z = 2 * np.sin(np.linspace(0, 2 * np.pi, n_points))
|
|
408
|
+
|
|
409
|
+
density = 15 + 5 * np.sin(np.linspace(0, 2 * np.pi, n_points))
|
|
410
|
+
velocity = 450 + 100 * np.sin(np.linspace(0, np.pi, n_points))
|
|
411
|
+
temperature = 1e5 + 5e4 * np.sin(np.linspace(0, np.pi, n_points))
|
|
412
|
+
|
|
413
|
+
return pd.DataFrame(
|
|
414
|
+
{
|
|
415
|
+
"density": density + np.random.normal(0, 1, n_points),
|
|
416
|
+
"velocity": velocity + np.random.normal(0, 20, n_points),
|
|
417
|
+
"temperature": temperature + np.random.normal(0, 1e4, n_points),
|
|
418
|
+
"b_x": b_x + np.random.normal(0, 0.5, n_points),
|
|
419
|
+
"b_y": b_y + np.random.normal(0, 0.5, n_points),
|
|
420
|
+
"b_z": b_z + np.random.normal(0, 0.5, n_points),
|
|
421
|
+
},
|
|
422
|
+
index=times,
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
return pd.DataFrame(
|
|
426
|
+
{"density": density, "velocity": velocity, "temperature": temperature},
|
|
427
|
+
index=times,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
@pytest.fixture(scope="function", params=["shock", "cme"])
|
|
432
|
+
def solar_wind_event(request):
|
|
433
|
+
"""Generate solar wind event data."""
|
|
434
|
+
return generate_solar_wind_event(event_type=request.param)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
# ============================================================================
|
|
438
|
+
# Performance Testing Utilities
|
|
439
|
+
# ============================================================================
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
class PerformanceTracker:
|
|
443
|
+
"""Track performance metrics for plotting operations."""
|
|
444
|
+
|
|
445
|
+
def __init__(self):
|
|
446
|
+
self.metrics = {}
|
|
447
|
+
|
|
448
|
+
def time_operation(self, operation_name, func, *args, **kwargs):
|
|
449
|
+
"""Time a plotting operation and store metrics."""
|
|
450
|
+
import time
|
|
451
|
+
|
|
452
|
+
start_time = time.time()
|
|
453
|
+
result = func(*args, **kwargs)
|
|
454
|
+
end_time = time.time()
|
|
455
|
+
|
|
456
|
+
elapsed = end_time - start_time
|
|
457
|
+
|
|
458
|
+
if operation_name not in self.metrics:
|
|
459
|
+
self.metrics[operation_name] = []
|
|
460
|
+
self.metrics[operation_name].append(elapsed)
|
|
461
|
+
|
|
462
|
+
return result, elapsed
|
|
463
|
+
|
|
464
|
+
def get_average_time(self, operation_name):
|
|
465
|
+
"""Get average time for an operation."""
|
|
466
|
+
if operation_name in self.metrics:
|
|
467
|
+
return np.mean(self.metrics[operation_name])
|
|
468
|
+
return None
|
|
469
|
+
|
|
470
|
+
def assert_performance(self, operation_name, max_time):
|
|
471
|
+
"""Assert operation performance meets expectations."""
|
|
472
|
+
avg_time = self.get_average_time(operation_name)
|
|
473
|
+
if avg_time is not None:
|
|
474
|
+
assert (
|
|
475
|
+
avg_time < max_time
|
|
476
|
+
), f"{operation_name} took {avg_time:.3f}s (expected < {max_time}s)"
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
@pytest.fixture(scope="function")
|
|
480
|
+
def performance_tracker():
|
|
481
|
+
"""Provide PerformanceTracker instance."""
|
|
482
|
+
return PerformanceTracker()
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
# ============================================================================
|
|
486
|
+
# Test Infrastructure Classes
|
|
487
|
+
# ============================================================================
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
class TestFixturesAndUtilities:
|
|
491
|
+
"""Test the test infrastructure itself."""
|
|
492
|
+
|
|
493
|
+
def test_synthetic_time_series_fixture(self, synthetic_time_series):
|
|
494
|
+
"""Test synthetic time series data fixture."""
|
|
495
|
+
assert isinstance(synthetic_time_series, pd.DataFrame)
|
|
496
|
+
assert len(synthetic_time_series) == 1000
|
|
497
|
+
assert synthetic_time_series.index.freq is not None
|
|
498
|
+
|
|
499
|
+
expected_columns = ["density", "velocity", "temperature", "b_field", "pressure"]
|
|
500
|
+
for col in expected_columns:
|
|
501
|
+
assert col in synthetic_time_series.columns
|
|
502
|
+
assert not synthetic_time_series[col].isna().any()
|
|
503
|
+
|
|
504
|
+
def test_synthetic_plasma_data_fixture(self, synthetic_plasma_data):
|
|
505
|
+
"""Test synthetic plasma data fixture."""
|
|
506
|
+
assert isinstance(synthetic_plasma_data, pd.DataFrame)
|
|
507
|
+
assert len(synthetic_plasma_data) == 500
|
|
508
|
+
|
|
509
|
+
# Check that correlations exist (anti-correlation between density and velocity)
|
|
510
|
+
density_velocity_corr = synthetic_plasma_data["density"].corr(
|
|
511
|
+
synthetic_plasma_data["velocity"]
|
|
512
|
+
)
|
|
513
|
+
assert (
|
|
514
|
+
density_velocity_corr < 0
|
|
515
|
+
), "Expected anti-correlation between density and velocity"
|
|
516
|
+
|
|
517
|
+
def test_plot_helper_functionality(self, plot_helper, single_axis):
|
|
518
|
+
"""Test PlotTestHelper functionality."""
|
|
519
|
+
fig, ax = single_axis
|
|
520
|
+
|
|
521
|
+
# Create a simple plot
|
|
522
|
+
x = np.linspace(0, 10, 100)
|
|
523
|
+
y = np.sin(x)
|
|
524
|
+
ax.plot(x, y, label="sin(x)")
|
|
525
|
+
ax.set_xlabel("X")
|
|
526
|
+
ax.set_ylabel("Y")
|
|
527
|
+
ax.set_title("Test Plot")
|
|
528
|
+
ax.legend()
|
|
529
|
+
ax.grid(True)
|
|
530
|
+
|
|
531
|
+
# Test assertions
|
|
532
|
+
plot_helper.assert_plot_elements(
|
|
533
|
+
ax, expected_lines=1, has_legend=True, has_grid=True
|
|
534
|
+
)
|
|
535
|
+
plot_helper.assert_axes_labels(ax, xlabel="X", ylabel="Y", title="Test Plot")
|
|
536
|
+
|
|
537
|
+
def test_temporary_file_fixtures(self, temp_dir, temp_plot_file, temp_data_file):
|
|
538
|
+
"""Test temporary file fixtures."""
|
|
539
|
+
assert temp_dir.exists()
|
|
540
|
+
assert temp_dir.is_dir()
|
|
541
|
+
|
|
542
|
+
# Test that parent directory of temp files exists
|
|
543
|
+
assert temp_plot_file.parent.exists()
|
|
544
|
+
assert temp_data_file.parent.exists()
|
|
545
|
+
|
|
546
|
+
# Test file creation
|
|
547
|
+
temp_plot_file.write_text("test plot data")
|
|
548
|
+
temp_data_file.write_text("test,data\n1,2\n")
|
|
549
|
+
|
|
550
|
+
assert temp_plot_file.exists()
|
|
551
|
+
assert temp_data_file.exists()
|
|
552
|
+
|
|
553
|
+
def test_dataset_fixtures(self, small_dataset, medium_dataset, large_dataset):
|
|
554
|
+
"""Test dataset fixtures have expected properties."""
|
|
555
|
+
x_small, y_small = small_dataset
|
|
556
|
+
x_medium, y_medium = medium_dataset
|
|
557
|
+
x_large, y_large = large_dataset
|
|
558
|
+
|
|
559
|
+
# Check sizes
|
|
560
|
+
assert len(x_small) == 50
|
|
561
|
+
assert len(x_medium) == 1000
|
|
562
|
+
assert len(x_large) == 100000
|
|
563
|
+
|
|
564
|
+
# Check data types
|
|
565
|
+
for x, y in [(x_small, y_small), (x_medium, y_medium), (x_large, y_large)]:
|
|
566
|
+
assert isinstance(x, np.ndarray)
|
|
567
|
+
assert isinstance(y, np.ndarray)
|
|
568
|
+
assert len(x) == len(y)
|
|
569
|
+
|
|
570
|
+
def test_scatter_data_fixture(self, scatter_data):
|
|
571
|
+
"""Test scatter data fixture."""
|
|
572
|
+
x, y, colors, sizes = scatter_data
|
|
573
|
+
|
|
574
|
+
assert len(x) == len(y) == len(colors) == len(sizes) == 200
|
|
575
|
+
assert 0 <= colors.min() and colors.max() <= 1 # Color values in [0,1]
|
|
576
|
+
assert sizes.min() >= 20 and sizes.max() <= 70 # Size range
|
|
577
|
+
|
|
578
|
+
def test_histogram_data_fixture(self, histogram_data):
|
|
579
|
+
"""Test histogram data fixture."""
|
|
580
|
+
expected_types = ["normal", "exponential", "uniform", "mixed"]
|
|
581
|
+
|
|
582
|
+
for data_type in expected_types:
|
|
583
|
+
assert data_type in histogram_data
|
|
584
|
+
data = histogram_data[data_type]
|
|
585
|
+
assert len(data) == 1000 or (data_type == "mixed" and len(data) == 1000)
|
|
586
|
+
|
|
587
|
+
def test_contour_data_fixture(self, contour_data):
|
|
588
|
+
"""Test contour data fixture."""
|
|
589
|
+
X, Y, Z = contour_data
|
|
590
|
+
|
|
591
|
+
assert X.shape == Y.shape == Z.shape == (50, 50)
|
|
592
|
+
assert not np.isnan(Z).any()
|
|
593
|
+
assert not np.isinf(Z).any()
|
|
594
|
+
|
|
595
|
+
def test_performance_tracker(self, performance_tracker):
|
|
596
|
+
"""Test performance tracking functionality."""
|
|
597
|
+
import time
|
|
598
|
+
|
|
599
|
+
def slow_operation():
|
|
600
|
+
time.sleep(0.1)
|
|
601
|
+
return "result"
|
|
602
|
+
|
|
603
|
+
def fast_operation():
|
|
604
|
+
return "result"
|
|
605
|
+
|
|
606
|
+
# Time operations
|
|
607
|
+
result1, time1 = performance_tracker.time_operation("slow", slow_operation)
|
|
608
|
+
result2, time2 = performance_tracker.time_operation("fast", fast_operation)
|
|
609
|
+
|
|
610
|
+
assert result1 == "result"
|
|
611
|
+
assert result2 == "result"
|
|
612
|
+
assert time1 > time2
|
|
613
|
+
assert time1 >= 0.1
|
|
614
|
+
|
|
615
|
+
# Test performance assertions
|
|
616
|
+
performance_tracker.assert_performance("fast", 0.1)
|
|
617
|
+
|
|
618
|
+
with pytest.raises(AssertionError):
|
|
619
|
+
performance_tracker.assert_performance("slow", 0.05)
|
|
620
|
+
|
|
621
|
+
def test_solar_wind_event_generator(self, solar_wind_event):
|
|
622
|
+
"""Test solar wind event data generator."""
|
|
623
|
+
assert isinstance(solar_wind_event, pd.DataFrame)
|
|
624
|
+
assert "density" in solar_wind_event.columns
|
|
625
|
+
assert "velocity" in solar_wind_event.columns
|
|
626
|
+
assert "temperature" in solar_wind_event.columns
|
|
627
|
+
|
|
628
|
+
# Check that data shows event characteristics
|
|
629
|
+
density_std = solar_wind_event["density"].std()
|
|
630
|
+
velocity_std = solar_wind_event["velocity"].std()
|
|
631
|
+
|
|
632
|
+
# Events should show significant variation
|
|
633
|
+
assert density_std > 1.0
|
|
634
|
+
assert velocity_std > 10.0
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
class TestMockingUtilities:
|
|
638
|
+
"""Test mocking utilities for unit testing."""
|
|
639
|
+
|
|
640
|
+
def test_mock_figure_fixture(self, mock_figure):
|
|
641
|
+
"""Test mock figure fixture."""
|
|
642
|
+
assert mock_figure is not None
|
|
643
|
+
|
|
644
|
+
# Test that mock can be called
|
|
645
|
+
mock_figure.savefig("test.png")
|
|
646
|
+
mock_figure.savefig.assert_called_with("test.png")
|
|
647
|
+
|
|
648
|
+
def test_mock_axes_fixture(self, mock_axes):
|
|
649
|
+
"""Test mock axes fixture."""
|
|
650
|
+
assert mock_axes is not None
|
|
651
|
+
|
|
652
|
+
# Test plotting methods return expected types
|
|
653
|
+
line = mock_axes.plot([1, 2, 3], [1, 2, 3])
|
|
654
|
+
assert line is not None
|
|
655
|
+
|
|
656
|
+
scatter = mock_axes.scatter([1, 2, 3], [1, 2, 3])
|
|
657
|
+
assert scatter is not None
|
|
658
|
+
|
|
659
|
+
hist_result = mock_axes.hist([1, 2, 3])
|
|
660
|
+
assert len(hist_result) == 3 # n, bins, patches
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
class TestFixtureIntegration:
|
|
664
|
+
"""Test integration between different fixtures."""
|
|
665
|
+
|
|
666
|
+
def test_plot_with_synthetic_data(
|
|
667
|
+
self, single_axis, synthetic_time_series, plot_helper
|
|
668
|
+
):
|
|
669
|
+
"""Test plotting with synthetic data using fixtures."""
|
|
670
|
+
fig, ax = single_axis
|
|
671
|
+
|
|
672
|
+
# Plot first parameter
|
|
673
|
+
param = "density"
|
|
674
|
+
ax.plot(
|
|
675
|
+
synthetic_time_series.index,
|
|
676
|
+
synthetic_time_series[param],
|
|
677
|
+
label=param,
|
|
678
|
+
linewidth=1.5,
|
|
679
|
+
)
|
|
680
|
+
ax.set_xlabel("Time")
|
|
681
|
+
ax.set_ylabel("Density (cm⁻³)")
|
|
682
|
+
ax.set_title("Synthetic Time Series Test")
|
|
683
|
+
ax.legend()
|
|
684
|
+
ax.grid(True, alpha=0.3)
|
|
685
|
+
|
|
686
|
+
# Use helper to validate
|
|
687
|
+
plot_helper.assert_plot_elements(
|
|
688
|
+
ax, expected_lines=1, has_legend=True, has_grid=True
|
|
689
|
+
)
|
|
690
|
+
plot_helper.assert_axes_labels(
|
|
691
|
+
ax,
|
|
692
|
+
xlabel="Time",
|
|
693
|
+
ylabel="Density (cm⁻³)",
|
|
694
|
+
title="Synthetic Time Series Test",
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
def test_save_plot_with_temp_files(
|
|
698
|
+
self, single_axis, small_dataset, temp_dir, plot_helper
|
|
699
|
+
):
|
|
700
|
+
"""Test saving plots using temporary file fixtures."""
|
|
701
|
+
fig, ax = single_axis
|
|
702
|
+
x, y = small_dataset
|
|
703
|
+
|
|
704
|
+
ax.plot(x, y, "b-", linewidth=2)
|
|
705
|
+
ax.set_xlabel("X")
|
|
706
|
+
ax.set_ylabel("Y")
|
|
707
|
+
ax.set_title("Save Test Plot")
|
|
708
|
+
|
|
709
|
+
# Save using helper
|
|
710
|
+
plot_file = temp_dir / "integration_test"
|
|
711
|
+
saved_files = plot_helper.save_and_verify_plot(
|
|
712
|
+
fig, plot_file, formats=["png", "pdf"]
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
assert len(saved_files) == 2
|
|
716
|
+
for file_path in saved_files:
|
|
717
|
+
assert file_path.exists()
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
def pytest_configure(config):
|
|
721
|
+
"""Configure pytest with custom markers."""
|
|
722
|
+
config.addinivalue_line(
|
|
723
|
+
"markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')"
|
|
724
|
+
)
|
|
725
|
+
config.addinivalue_line("markers", "integration: marks tests as integration tests")
|
|
726
|
+
config.addinivalue_line("markers", "performance: marks tests as performance tests")
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
# ============================================================================
|
|
730
|
+
# Utility Functions for External Use
|
|
731
|
+
# ============================================================================
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
def create_test_plot(plot_type="line", data_size="medium", **kwargs):
|
|
735
|
+
"""Create a test plot for external testing use."""
|
|
736
|
+
if data_size == "small":
|
|
737
|
+
n = 50
|
|
738
|
+
elif data_size == "medium":
|
|
739
|
+
n = 1000
|
|
740
|
+
elif data_size == "large":
|
|
741
|
+
n = 10000
|
|
742
|
+
else:
|
|
743
|
+
n = int(data_size)
|
|
744
|
+
|
|
745
|
+
fig, ax = plt.subplots(figsize=kwargs.get("figsize", (8, 6)))
|
|
746
|
+
|
|
747
|
+
if plot_type == "line":
|
|
748
|
+
x = np.linspace(0, 10, n)
|
|
749
|
+
y = np.sin(x) + 0.1 * np.random.randn(n)
|
|
750
|
+
ax.plot(x, y, **kwargs)
|
|
751
|
+
|
|
752
|
+
elif plot_type == "scatter":
|
|
753
|
+
x = np.random.randn(n)
|
|
754
|
+
y = np.random.randn(n)
|
|
755
|
+
ax.scatter(x, y, **kwargs)
|
|
756
|
+
|
|
757
|
+
elif plot_type == "histogram":
|
|
758
|
+
data = np.random.normal(0, 1, n)
|
|
759
|
+
ax.hist(data, bins=kwargs.get("bins", 30), **kwargs)
|
|
760
|
+
|
|
761
|
+
return fig, ax
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
def validate_plot_output(ax, plot_type="line"):
|
|
765
|
+
"""Validate that plot output meets basic requirements."""
|
|
766
|
+
if plot_type == "line":
|
|
767
|
+
assert len(ax.lines) > 0, "No lines found in line plot"
|
|
768
|
+
elif plot_type == "scatter":
|
|
769
|
+
assert len(ax.collections) > 0, "No scatter collections found"
|
|
770
|
+
elif plot_type == "histogram":
|
|
771
|
+
assert len(ax.patches) > 0, "No histogram patches found"
|
|
772
|
+
|
|
773
|
+
# General validations
|
|
774
|
+
assert ax.get_xlabel() != "" or ax.get_ylabel() != "", "No axis labels set"
|
|
775
|
+
return True
|