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,734 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
r"""Base classes used to implement specific fit functions.
|
|
3
|
+
|
|
4
|
+
The :class:`FitFunction` abstract base class handles selecting the
|
|
5
|
+
observations to include in a fit, running SciPy optimizers and
|
|
6
|
+
providing convenient plotting helpers. Subclasses need only define
|
|
7
|
+
the functional form and an initial parameter guess.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import pdb # noqa: F401
|
|
11
|
+
import logging # noqa: F401
|
|
12
|
+
import warnings
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from abc import ABC, abstractproperty
|
|
16
|
+
from collections import namedtuple
|
|
17
|
+
from inspect import getfullargspec
|
|
18
|
+
|
|
19
|
+
# from scipy.optimize import curve_fit
|
|
20
|
+
from scipy.optimize import least_squares, OptimizeWarning
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
from scipy.optimize._minpack_py import (
|
|
24
|
+
_wrap_func,
|
|
25
|
+
_wrap_jac,
|
|
26
|
+
_initialize_feasible,
|
|
27
|
+
)
|
|
28
|
+
except ImportError: # pragma: no cover - fall back for older SciPy versions
|
|
29
|
+
from scipy.optimize.minpack import (
|
|
30
|
+
_wrap_func,
|
|
31
|
+
_wrap_jac,
|
|
32
|
+
_initialize_feasible,
|
|
33
|
+
)
|
|
34
|
+
from scipy.optimize._lsq.least_squares import prepare_bounds
|
|
35
|
+
from scipy.linalg import svd, cholesky, LinAlgError
|
|
36
|
+
|
|
37
|
+
from .tex_info import TeXinfo
|
|
38
|
+
from .plots import FFPlot
|
|
39
|
+
|
|
40
|
+
Observations = namedtuple("Observations", "x,y,w")
|
|
41
|
+
UsedRawObs = namedtuple("UsedRawObs", "used,raw,tk_observed")
|
|
42
|
+
InitialGuessInfo = namedtuple("InitialGuessInfo", "p0,bounds")
|
|
43
|
+
ChisqPerDegreeOfFreedom = namedtuple("ChisqPerDegreeOfFreedom", "linear,robust")
|
|
44
|
+
FitBounds = namedtuple("FitBounds", "lower,upper")
|
|
45
|
+
|
|
46
|
+
# def __huber(z):
|
|
47
|
+
# cost = np.array(z)
|
|
48
|
+
# mask = z <= 1
|
|
49
|
+
# cost[~mask] = 2 * z[~mask]**0.5 - 1
|
|
50
|
+
# return cost
|
|
51
|
+
#
|
|
52
|
+
# def __soft_l1(z):
|
|
53
|
+
# t = 1 + z
|
|
54
|
+
# cost = 2 * (t**0.5 - 1)
|
|
55
|
+
# return cost
|
|
56
|
+
#
|
|
57
|
+
# _loss_fcns = {"huber": __huber,
|
|
58
|
+
# "soft_l1": __soft_l1,
|
|
59
|
+
# "cauchy": np.log1p,
|
|
60
|
+
# "arctan": np.arctan}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class FitFunction(ABC):
|
|
64
|
+
r"""Assuming that you don't want special formatting, call order is:
|
|
65
|
+
|
|
66
|
+
fit_function = FitFunction(function, TeX_string)
|
|
67
|
+
fit_function.make_fit()
|
|
68
|
+
|
|
69
|
+
Instances are callable. If the fit fails, calling the instance will return
|
|
70
|
+
an array of NaNs the same shape as the x-values.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
xobs,
|
|
76
|
+
yobs,
|
|
77
|
+
xmin=None,
|
|
78
|
+
xmax=None,
|
|
79
|
+
xoutside=None,
|
|
80
|
+
ymin=None,
|
|
81
|
+
ymax=None,
|
|
82
|
+
youtside=None,
|
|
83
|
+
weights=None,
|
|
84
|
+
wmin=None,
|
|
85
|
+
wmax=None,
|
|
86
|
+
logx=False,
|
|
87
|
+
logy=False,
|
|
88
|
+
):
|
|
89
|
+
"""Initialize a ``FitFunction`` with observed data.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
xobs, yobs : array-like
|
|
94
|
+
Observed ``x`` and ``y`` values.
|
|
95
|
+
xmin, xmax : float, optional
|
|
96
|
+
Range limits for ``x`` used in fitting.
|
|
97
|
+
xoutside : tuple(float, float), optional
|
|
98
|
+
Include data outside this range in the fit.
|
|
99
|
+
ymin, ymax : float, optional
|
|
100
|
+
Range limits for ``y`` used in fitting.
|
|
101
|
+
youtside : tuple(float, float), optional
|
|
102
|
+
Include data outside this range.
|
|
103
|
+
weights : array-like, optional
|
|
104
|
+
Uncertainties associated with ``y``.
|
|
105
|
+
wmin, wmax : float, optional
|
|
106
|
+
Weight limits.
|
|
107
|
+
logx, logy : bool, default False
|
|
108
|
+
Whether to interpret ``x`` or ``y`` on a log10 scale.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
self._init_logger()
|
|
112
|
+
self._set_argnames()
|
|
113
|
+
|
|
114
|
+
if weights is None:
|
|
115
|
+
assert wmin is None
|
|
116
|
+
assert wmax is None
|
|
117
|
+
|
|
118
|
+
self.set_fit_obs(
|
|
119
|
+
xobs,
|
|
120
|
+
yobs,
|
|
121
|
+
weights,
|
|
122
|
+
xmin=xmin,
|
|
123
|
+
xmax=xmax,
|
|
124
|
+
xoutside=xoutside,
|
|
125
|
+
ymin=ymin,
|
|
126
|
+
ymax=ymax,
|
|
127
|
+
youtside=youtside,
|
|
128
|
+
wmin=wmin,
|
|
129
|
+
wmax=wmax,
|
|
130
|
+
logx=logx,
|
|
131
|
+
logy=logy,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def __str__(self):
|
|
135
|
+
return f"{self.__class__.__name__} ({self.TeX_function})"
|
|
136
|
+
|
|
137
|
+
def __call__(self, x):
|
|
138
|
+
"""Evaluate the fitted model at ``x``."""
|
|
139
|
+
|
|
140
|
+
# TODO
|
|
141
|
+
# Do you want to have this function accept optional kwarg parameters?
|
|
142
|
+
# It adds a layer of complexity, but could be helfpul.
|
|
143
|
+
|
|
144
|
+
# Sort the parameter keywords into the proper order to pass to the
|
|
145
|
+
# numerical function.
|
|
146
|
+
|
|
147
|
+
# try:
|
|
148
|
+
popt_ = self.popt
|
|
149
|
+
popt_ = [popt_[k] for k in self.argnames]
|
|
150
|
+
|
|
151
|
+
# NOTE
|
|
152
|
+
# An instance of FitFunction is for a given function. To change the
|
|
153
|
+
# function itself, a new instance of FitFunction should be required.
|
|
154
|
+
# Therefore, we access the function directly.
|
|
155
|
+
y = self.function(x, *popt_)
|
|
156
|
+
|
|
157
|
+
# except AttributeError as e:
|
|
158
|
+
# if "'PowerLaw' object has no attribute '_popt'" in str(e):
|
|
159
|
+
# y = np.full_like(x, np.nan, dtype=np.float64)
|
|
160
|
+
|
|
161
|
+
return y
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def logger(self):
|
|
165
|
+
return self._logger
|
|
166
|
+
|
|
167
|
+
def _init_logger(self):
|
|
168
|
+
"""Create a module-level logger for the instance."""
|
|
169
|
+
|
|
170
|
+
logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
|
|
171
|
+
self._logger = logger
|
|
172
|
+
|
|
173
|
+
@abstractproperty
|
|
174
|
+
def function(self):
|
|
175
|
+
r"""Get the function that`curve_fit` fits.
|
|
176
|
+
|
|
177
|
+
The function is set at instantiation. It doesn't make sense to change
|
|
178
|
+
it unless you redefine the entire FitFunction, so there is no `new`
|
|
179
|
+
kwarg.
|
|
180
|
+
"""
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
@abstractproperty
|
|
184
|
+
def p0(self):
|
|
185
|
+
r"""The initial guess for the FitFunction."""
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
@abstractproperty
|
|
189
|
+
def TeX_function(self):
|
|
190
|
+
r"""Function written in LaTeX."""
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def argnames(self):
|
|
195
|
+
r"""The names of the actual function arguments pulled by getfullargspec."""
|
|
196
|
+
return self._argnames
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def fit_bounds(self):
|
|
200
|
+
r"""Bounds used when running the fit."""
|
|
201
|
+
return dict(self._fit_bounds)
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def chisq_dof(self):
|
|
205
|
+
r"""Chisq per degree of freedom :math:`\chi^2_\nu`.
|
|
206
|
+
|
|
207
|
+
If None, not calculated by `make_fit_old`. If `np.nan`, fit failed.
|
|
208
|
+
"""
|
|
209
|
+
# r = self.residuals(pct=False)
|
|
210
|
+
# sigma = self.observations.used.w
|
|
211
|
+
# if sigma is not None:
|
|
212
|
+
# r = r / sigma
|
|
213
|
+
#
|
|
214
|
+
# chisq = (r ** 2).sum()
|
|
215
|
+
# dof = r.size - len(self.p0)
|
|
216
|
+
# chisq_dof = chisq / dof
|
|
217
|
+
# return chisq_dof
|
|
218
|
+
try:
|
|
219
|
+
return self._chisq_dof
|
|
220
|
+
except AttributeError:
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def dof(self):
|
|
225
|
+
r"""Degrees of freedom in the fit."""
|
|
226
|
+
return self.observations.used.y.size - len(self.p0)
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def fit_result(self):
|
|
230
|
+
return self._fit_result
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def initial_guess_info(self):
|
|
234
|
+
|
|
235
|
+
# If failed to make an initial guess, then don't build the info.
|
|
236
|
+
try:
|
|
237
|
+
p0 = self.p0
|
|
238
|
+
bounds = self.fit_bounds
|
|
239
|
+
except AttributeError:
|
|
240
|
+
return None
|
|
241
|
+
|
|
242
|
+
names = self.argnames
|
|
243
|
+
info = {
|
|
244
|
+
name: InitialGuessInfo(guess, tuple(bounds[name]))
|
|
245
|
+
for name, guess in zip(names, p0)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
# info = ["\n".join(param) for param in info]
|
|
249
|
+
# info = "\n\n".join(info)
|
|
250
|
+
|
|
251
|
+
return info
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def nobs(self):
|
|
255
|
+
r"""The total number of observations used in the fit."""
|
|
256
|
+
return self.observations.tk_observed.sum()
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def observations(self):
|
|
260
|
+
return self._observations
|
|
261
|
+
|
|
262
|
+
@property
|
|
263
|
+
def plotter(self):
|
|
264
|
+
# try:
|
|
265
|
+
return self._plotter
|
|
266
|
+
|
|
267
|
+
# except AttributeError:
|
|
268
|
+
# return self.build_plotter()
|
|
269
|
+
|
|
270
|
+
@property
|
|
271
|
+
def popt(self):
|
|
272
|
+
r"""Optimized fit parameters."""
|
|
273
|
+
return dict(self._popt)
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def psigma(self):
|
|
277
|
+
return dict(self._psigma)
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def psigma_relative(self):
|
|
281
|
+
return {k: v / self.popt[k] for k, v in self.psigma.items()}
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def combined_popt_psigma(self):
|
|
285
|
+
r"""Convenience to extract all versions of the optimized parameters."""
|
|
286
|
+
# try:
|
|
287
|
+
popt = self.popt
|
|
288
|
+
psigma = self.psigma
|
|
289
|
+
prel = self.psigma_relative
|
|
290
|
+
# except AttributeError:
|
|
291
|
+
# popt = {k: np.nan for k in self.argnames}
|
|
292
|
+
# psigma = {k: np.nan for k in self.argnames}
|
|
293
|
+
# prel = {k: np.nan for k in self.argnames}
|
|
294
|
+
|
|
295
|
+
return {"popt": popt, "psigma": psigma, "psigma_relative": prel}
|
|
296
|
+
|
|
297
|
+
@property
|
|
298
|
+
def pcov(self):
|
|
299
|
+
r"""Returns a copy so that the matrix isn't accidentally edited."""
|
|
300
|
+
return self._pcov.copy()
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def rsq(self):
|
|
304
|
+
r"""Coefficient of determination.
|
|
305
|
+
|
|
306
|
+
Source: <en.wikipedia.org/wiki/Coefficient_of_determination#Definitions>
|
|
307
|
+
"""
|
|
308
|
+
y = self.observations.used.y
|
|
309
|
+
ybar = y.mean()
|
|
310
|
+
yfit = self(self.observations.used.x)
|
|
311
|
+
sum_squares_total = ((y - ybar) ** 2).sum()
|
|
312
|
+
sum_squares_residual = ((y - yfit) ** 2).sum()
|
|
313
|
+
rsq = 1 - (sum_squares_residual / sum_squares_total)
|
|
314
|
+
|
|
315
|
+
return rsq
|
|
316
|
+
|
|
317
|
+
@property
|
|
318
|
+
def sufficient_data(self):
|
|
319
|
+
r"""Ensure that we can fit the data before doing any computations."""
|
|
320
|
+
chk = self.nobs >= len(self.argnames)
|
|
321
|
+
if not chk:
|
|
322
|
+
msg = "There is insufficient data to fit the model."
|
|
323
|
+
raise ValueError(msg)
|
|
324
|
+
else:
|
|
325
|
+
return True
|
|
326
|
+
|
|
327
|
+
@property
|
|
328
|
+
def TeX_info(self):
|
|
329
|
+
# try:
|
|
330
|
+
return self._TeX_info
|
|
331
|
+
|
|
332
|
+
# except AttributeError:
|
|
333
|
+
# return self.build_TeX_info()
|
|
334
|
+
|
|
335
|
+
def _clean_raw_obs(self, xobs, yobs, weights):
|
|
336
|
+
r"""Set the raw x- and y-values along with weights for the fit.
|
|
337
|
+
|
|
338
|
+
Doesn't account for extrema, finite data, etc.
|
|
339
|
+
"""
|
|
340
|
+
xobs = np.asarray(xobs)
|
|
341
|
+
yobs = np.asarray(yobs)
|
|
342
|
+
if weights is not None:
|
|
343
|
+
weights = np.asarray(weights)
|
|
344
|
+
|
|
345
|
+
if xobs.shape != yobs.shape:
|
|
346
|
+
raise ValueError(
|
|
347
|
+
f"""xobs and yobs must have the same shape.,
|
|
348
|
+
xobs: {xobs.shape},
|
|
349
|
+
yobs: {yobs.shape}"""
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
if weights is not None and weights.shape != xobs.shape:
|
|
353
|
+
raise ValueError(
|
|
354
|
+
f"""weights and xobs must have the same shape.,
|
|
355
|
+
weighs: {weights.shape}",
|
|
356
|
+
xobs: {xobs.shape}"""
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
return xobs, yobs, weights
|
|
360
|
+
|
|
361
|
+
def _build_one_obs_mask(self, axis, x, xmin, xmax):
|
|
362
|
+
# mask = np.full_like(x, True, dtype=bool)
|
|
363
|
+
|
|
364
|
+
mask = np.isfinite(x)
|
|
365
|
+
|
|
366
|
+
if xmin is not None:
|
|
367
|
+
xmin_mask = x >= xmin
|
|
368
|
+
mask = mask & xmin_mask
|
|
369
|
+
|
|
370
|
+
if xmax is not None:
|
|
371
|
+
xmax_mask = x <= xmax
|
|
372
|
+
mask = mask & xmax_mask
|
|
373
|
+
|
|
374
|
+
return mask
|
|
375
|
+
|
|
376
|
+
def _build_outside_mask(self, axis, x, outside):
|
|
377
|
+
r"""Take data outside of the range `outside[0]:outside[1]`."""
|
|
378
|
+
|
|
379
|
+
if outside is None:
|
|
380
|
+
return np.full_like(x, True, dtype=bool)
|
|
381
|
+
|
|
382
|
+
lower, upper = outside
|
|
383
|
+
assert lower < upper
|
|
384
|
+
l_mask = x <= lower
|
|
385
|
+
u_mask = x >= upper
|
|
386
|
+
mask = l_mask | u_mask
|
|
387
|
+
|
|
388
|
+
return mask
|
|
389
|
+
|
|
390
|
+
def _set_argnames(self):
|
|
391
|
+
r"""Set the arguments of the function/
|
|
392
|
+
|
|
393
|
+
Assume that the first is dependent variable.
|
|
394
|
+
Should be called after function is set.
|
|
395
|
+
"""
|
|
396
|
+
args = getfullargspec(self.function).args[1:]
|
|
397
|
+
self._argnames = args
|
|
398
|
+
|
|
399
|
+
def build_plotter(self):
|
|
400
|
+
obs = self.observations
|
|
401
|
+
try:
|
|
402
|
+
yfit = self(self.observations.raw.x)
|
|
403
|
+
except AttributeError:
|
|
404
|
+
yfit = np.full_like(self.observations.raw.x, np.nan)
|
|
405
|
+
# robust_residuals = self.fit_result.fun
|
|
406
|
+
tex_info = self.TeX_info
|
|
407
|
+
fit_result = self.fit_result
|
|
408
|
+
|
|
409
|
+
# try:
|
|
410
|
+
# fit_result = self.fit_result
|
|
411
|
+
# except AttributeError:
|
|
412
|
+
# fit_result = None
|
|
413
|
+
|
|
414
|
+
plotter = FFPlot(
|
|
415
|
+
obs,
|
|
416
|
+
yfit,
|
|
417
|
+
# robust_residuals,
|
|
418
|
+
tex_info,
|
|
419
|
+
fit_result,
|
|
420
|
+
fitfunction_name=self.__class__.__name__,
|
|
421
|
+
)
|
|
422
|
+
self._plotter = plotter
|
|
423
|
+
return plotter
|
|
424
|
+
|
|
425
|
+
def build_TeX_info(self):
|
|
426
|
+
|
|
427
|
+
# Allows annotating of TeX_info when fit fails in a manner
|
|
428
|
+
# that is easily identifiable.
|
|
429
|
+
try:
|
|
430
|
+
popt = self.popt
|
|
431
|
+
except AttributeError:
|
|
432
|
+
popt = {k: np.nan for k in self.argnames}
|
|
433
|
+
|
|
434
|
+
try:
|
|
435
|
+
psigma = self.psigma
|
|
436
|
+
except AttributeError:
|
|
437
|
+
psigma = {k: np.nan for k in self.argnames}
|
|
438
|
+
|
|
439
|
+
tex_info = TeXinfo(
|
|
440
|
+
popt,
|
|
441
|
+
psigma,
|
|
442
|
+
self.TeX_function,
|
|
443
|
+
self.chisq_dof,
|
|
444
|
+
self.rsq,
|
|
445
|
+
initial_guess_info=self.initial_guess_info,
|
|
446
|
+
)
|
|
447
|
+
self._TeX_info = tex_info
|
|
448
|
+
return tex_info
|
|
449
|
+
|
|
450
|
+
def residuals(self, pct=False):
|
|
451
|
+
r"""Calculate the fit residuals.
|
|
452
|
+
|
|
453
|
+
If pct, normalize by fit yvalues.
|
|
454
|
+
"""
|
|
455
|
+
|
|
456
|
+
# TODO: calculate with all values
|
|
457
|
+
# Make it an option to calculate with either
|
|
458
|
+
# the values used in the fit or all the values,
|
|
459
|
+
# including those excluded by `set_extrema`.
|
|
460
|
+
|
|
461
|
+
r = self(self.observations.used.x) - self.observations.used.y
|
|
462
|
+
# r = self.fit_result.fun
|
|
463
|
+
|
|
464
|
+
if pct:
|
|
465
|
+
r = 100.0 * (r / self(self.observations.used.x))
|
|
466
|
+
|
|
467
|
+
return r
|
|
468
|
+
|
|
469
|
+
def set_fit_obs(
|
|
470
|
+
self,
|
|
471
|
+
xobs_raw,
|
|
472
|
+
yobs_raw,
|
|
473
|
+
weights_raw,
|
|
474
|
+
xmin=None,
|
|
475
|
+
xmax=None,
|
|
476
|
+
xoutside=None,
|
|
477
|
+
ymin=None,
|
|
478
|
+
ymax=None,
|
|
479
|
+
youtside=None,
|
|
480
|
+
wmin=None,
|
|
481
|
+
wmax=None,
|
|
482
|
+
logx=False,
|
|
483
|
+
logy=False,
|
|
484
|
+
):
|
|
485
|
+
r"""Set the observed values we'll actually use in the fit.
|
|
486
|
+
|
|
487
|
+
By applying limits to xobs_raw and yobs_raw and checking for finite values.
|
|
488
|
+
|
|
489
|
+
All boundaries are inclusive <= or >=.
|
|
490
|
+
|
|
491
|
+
If logy, then make selection of `wmin` and `wmax` based on :math:`w/(y \ln(10))`.
|
|
492
|
+
"""
|
|
493
|
+
|
|
494
|
+
xobs_raw, yobs_raw, weights_raw = self._clean_raw_obs(
|
|
495
|
+
xobs_raw, yobs_raw, weights_raw
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
xmask = self._build_one_obs_mask("xobs", xobs_raw, xmin, xmax)
|
|
499
|
+
ymask = self._build_one_obs_mask("yobs", yobs_raw, ymin, ymax)
|
|
500
|
+
xout_mask = self._build_outside_mask("xobs", xobs_raw, xoutside)
|
|
501
|
+
yout_mask = self._build_outside_mask("yobs", yobs_raw, youtside)
|
|
502
|
+
|
|
503
|
+
mask = xmask & ymask & xout_mask & yout_mask
|
|
504
|
+
if weights_raw is not None:
|
|
505
|
+
weights_for_mask = weights_raw
|
|
506
|
+
|
|
507
|
+
if logy:
|
|
508
|
+
# Enables picking weights based on normalized scale for log stuff
|
|
509
|
+
weights_for_mask = weights_for_mask / (yobs_raw * np.log(10))
|
|
510
|
+
|
|
511
|
+
wmask = self._build_one_obs_mask("weights", weights_for_mask, wmin, wmax)
|
|
512
|
+
mask = mask & wmask
|
|
513
|
+
|
|
514
|
+
xobs = xobs_raw[mask]
|
|
515
|
+
yobs = yobs_raw[mask]
|
|
516
|
+
weights = None
|
|
517
|
+
if weights_raw is not None:
|
|
518
|
+
weights = weights_raw[mask]
|
|
519
|
+
|
|
520
|
+
used = Observations(xobs, yobs, weights)
|
|
521
|
+
raw = Observations(xobs_raw, yobs_raw, weights_raw)
|
|
522
|
+
usedrawobs = UsedRawObs(used, raw, mask)
|
|
523
|
+
self._observations = usedrawobs
|
|
524
|
+
|
|
525
|
+
def _run_least_squares(self, **kwargs):
|
|
526
|
+
"""Execute :func:`scipy.optimize.least_squares` with defaults."""
|
|
527
|
+
|
|
528
|
+
p0 = kwargs.pop("p0", self.p0)
|
|
529
|
+
bounds = kwargs.pop("bounds", (-np.inf, np.inf))
|
|
530
|
+
method = kwargs.pop("method", "trf")
|
|
531
|
+
loss = kwargs.pop("loss", "huber")
|
|
532
|
+
max_nfev = kwargs.pop("max_nfev", 10000)
|
|
533
|
+
f_scale = kwargs.pop("f_scale", 0.1)
|
|
534
|
+
jac = kwargs.pop("jac", "2-point")
|
|
535
|
+
|
|
536
|
+
# loss_fcn = _loss_fcns.pop(loss, loss)
|
|
537
|
+
|
|
538
|
+
# Copied from `curve_fit` line 704 (20200527)
|
|
539
|
+
if p0 is None:
|
|
540
|
+
# determine number of parameters by inspecting the function
|
|
541
|
+
from scipy._lib._util import getargspec_no_self as _getargspec
|
|
542
|
+
|
|
543
|
+
args, varargs, varkw, defaults = _getargspec(self.function)
|
|
544
|
+
if len(args) < 2:
|
|
545
|
+
raise ValueError("Unable to determine number of fit parameters.")
|
|
546
|
+
n = len(args) - 1
|
|
547
|
+
else:
|
|
548
|
+
p0 = np.atleast_1d(p0)
|
|
549
|
+
n = p0.size
|
|
550
|
+
|
|
551
|
+
if isinstance(bounds, dict):
|
|
552
|
+
# Monkey patch to work with bounds being stored as
|
|
553
|
+
# dict for TeX_info. (20201202)
|
|
554
|
+
bounds = [bounds[k] for k in self.argnames]
|
|
555
|
+
bounds = np.array(bounds).T
|
|
556
|
+
|
|
557
|
+
# Copied from `curve_fit` line 715 (20200527)
|
|
558
|
+
lb, ub = prepare_bounds(bounds, n)
|
|
559
|
+
if p0 is None:
|
|
560
|
+
p0 = _initialize_feasible(lb, ub)
|
|
561
|
+
|
|
562
|
+
if "args" in kwargs:
|
|
563
|
+
raise ValueError(
|
|
564
|
+
"Adopted `curve_fit` convention for which'args' is not a supported keyword argument."
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
xdata = self.observations.used.x
|
|
568
|
+
ydata = self.observations.used.y
|
|
569
|
+
sigma = self.observations.used.w
|
|
570
|
+
|
|
571
|
+
# Copied from `curve_fit` line 749 (20200527)
|
|
572
|
+
# Determine type of sigma
|
|
573
|
+
if sigma is not None:
|
|
574
|
+
sigma = np.asarray(sigma)
|
|
575
|
+
# sigma = sigma / np.nansum(sigma)
|
|
576
|
+
|
|
577
|
+
# if 1-d, sigma are errors, define transform = 1/sigma
|
|
578
|
+
if sigma.shape == (ydata.size,):
|
|
579
|
+
transform = 1.0 / sigma
|
|
580
|
+
# if 2-d, sigma is the covariance matrix,
|
|
581
|
+
# define transform = L such that L L^T = C
|
|
582
|
+
elif sigma.shape == (ydata.size, ydata.size):
|
|
583
|
+
try:
|
|
584
|
+
# scipy.linalg.cholesky requires lower=True to return L L^T = A
|
|
585
|
+
transform = cholesky(sigma, lower=True)
|
|
586
|
+
except LinAlgError:
|
|
587
|
+
raise ValueError("`sigma` must be positive definite.")
|
|
588
|
+
else:
|
|
589
|
+
raise ValueError("`sigma` has incorrect shape.")
|
|
590
|
+
else:
|
|
591
|
+
transform = None
|
|
592
|
+
|
|
593
|
+
# Copied from `curve_fit` line 769 (20200527)
|
|
594
|
+
loss_func = _wrap_func(self.function, xdata, ydata, transform)
|
|
595
|
+
# Already define default `jac` with `kwargs`. Don't need ELSE clause.
|
|
596
|
+
if callable(jac):
|
|
597
|
+
jac = _wrap_jac(jac, xdata, transform)
|
|
598
|
+
|
|
599
|
+
res = least_squares(
|
|
600
|
+
loss_func,
|
|
601
|
+
p0,
|
|
602
|
+
jac=jac,
|
|
603
|
+
bounds=bounds,
|
|
604
|
+
method=method,
|
|
605
|
+
loss=loss,
|
|
606
|
+
max_nfev=max_nfev,
|
|
607
|
+
f_scale=f_scale,
|
|
608
|
+
**kwargs,
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
if not res.success:
|
|
612
|
+
raise RuntimeError("Optimal parameters not found: " + res.message)
|
|
613
|
+
|
|
614
|
+
fit_bounds = np.concatenate([lb, ub]).reshape((2, -1)).T
|
|
615
|
+
fit_bounds = {k: FitBounds(*b) for k, b in zip(self.argnames, fit_bounds)}
|
|
616
|
+
fit_bounds = tuple(fit_bounds.items())
|
|
617
|
+
self._fit_bounds = fit_bounds
|
|
618
|
+
|
|
619
|
+
# self._loss_fcn = loss_fcn
|
|
620
|
+
return res, p0
|
|
621
|
+
|
|
622
|
+
def _calc_popt_pcov_psigma_chisq(self, res, p0):
|
|
623
|
+
"""Compute optimized parameters and statistics from the result."""
|
|
624
|
+
|
|
625
|
+
xdata = self.observations.used.x
|
|
626
|
+
ydata = self.observations.used.y
|
|
627
|
+
sigma = self.observations.used.w
|
|
628
|
+
|
|
629
|
+
# The following is from `curve_fit` line 801 and following. (20200625)
|
|
630
|
+
# `cost` is the robust loss, i.e. residuals passed through loss funciton.
|
|
631
|
+
ysize = len(res.fun)
|
|
632
|
+
cost = 2 * res.cost # res.cost is half sum of squares!
|
|
633
|
+
popt = res.x
|
|
634
|
+
|
|
635
|
+
# Linear chisq_dof value.
|
|
636
|
+
dof = ydata.size - len(p0)
|
|
637
|
+
chisq_dof = np.inf # Divide by zero => infinity
|
|
638
|
+
if dof:
|
|
639
|
+
r = self.function(xdata, *popt) - ydata
|
|
640
|
+
if sigma is not None:
|
|
641
|
+
r /= sigma
|
|
642
|
+
chisq_dof = (r**2).sum() / dof
|
|
643
|
+
|
|
644
|
+
# Do Moore-Penrose inverse discarding zero singular values.
|
|
645
|
+
_, s, VT = svd(res.jac, full_matrices=False)
|
|
646
|
+
threshold = np.finfo(float).eps * max(res.jac.shape) * s[0]
|
|
647
|
+
s = s[s > threshold]
|
|
648
|
+
VT = VT[: s.size]
|
|
649
|
+
pcov = np.dot(VT.T / (s**2), VT)
|
|
650
|
+
|
|
651
|
+
warn_cov = False
|
|
652
|
+
if ysize > p0.size:
|
|
653
|
+
# Cost is robust residuals, so this is robust chisq per dof.
|
|
654
|
+
s_sq = cost / (ysize - p0.size)
|
|
655
|
+
pcov = pcov * s_sq
|
|
656
|
+
else:
|
|
657
|
+
s_sq = np.nan
|
|
658
|
+
pcov.fill(np.inf)
|
|
659
|
+
warn_cov = True
|
|
660
|
+
|
|
661
|
+
if warn_cov:
|
|
662
|
+
warnings.warn(
|
|
663
|
+
"Covariance of the parameters could not be estimated",
|
|
664
|
+
category=OptimizeWarning,
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
psigma = np.sqrt(np.diag(pcov))
|
|
668
|
+
|
|
669
|
+
# Based on `curve_fit`'s `absolute_sigma` documentation and reading
|
|
670
|
+
# `least_square`, `s_sq` should be chisq_nu based on robust residuals
|
|
671
|
+
# that account for `f_scale`(20200527).
|
|
672
|
+
all_chisq = ChisqPerDegreeOfFreedom(chisq_dof, s_sq)
|
|
673
|
+
|
|
674
|
+
return popt, pcov, psigma, all_chisq
|
|
675
|
+
|
|
676
|
+
def make_fit(self, return_exception=False, **kwargs):
|
|
677
|
+
"""Fit the function with the independent `xobs` and dependent `yobs`.
|
|
678
|
+
|
|
679
|
+
Uses `least_squares` and returns the `OptimizeResult` object, but
|
|
680
|
+
treats weights as in `curve_fit`.
|
|
681
|
+
|
|
682
|
+
Parameters
|
|
683
|
+
----------
|
|
684
|
+
return_exception: bool
|
|
685
|
+
If True, return exceptions from fitting routine, instead of raising.
|
|
686
|
+
This is useful when looping through many fits and wanting to
|
|
687
|
+
identify failed fits after the fact.
|
|
688
|
+
|
|
689
|
+
kwargs:
|
|
690
|
+
Unless specified here, defaults are as defined by `curve_fit`.
|
|
691
|
+
|
|
692
|
+
============= ======================================
|
|
693
|
+
kwarg default
|
|
694
|
+
============= ======================================
|
|
695
|
+
p0 Setup by `{self.__class__.__name__}`
|
|
696
|
+
return_full False
|
|
697
|
+
method "trf"
|
|
698
|
+
loss "huber"
|
|
699
|
+
max_nfev 10000
|
|
700
|
+
f_scale 0.1
|
|
701
|
+
============= ======================================
|
|
702
|
+
"""
|
|
703
|
+
try:
|
|
704
|
+
assert self.sufficient_data # Check we have enough data to fit.
|
|
705
|
+
except (AssertionError, ValueError) as e:
|
|
706
|
+
# raise
|
|
707
|
+
if isinstance(e, AssertionError):
|
|
708
|
+
e = ValueError("Insufficient data to fit the model")
|
|
709
|
+
return e
|
|
710
|
+
|
|
711
|
+
absolute_sigma = kwargs.pop("absolute_sigma", False)
|
|
712
|
+
if absolute_sigma:
|
|
713
|
+
raise NotImplementedError("We want to rescale fit errors by chisq_dof")
|
|
714
|
+
|
|
715
|
+
try:
|
|
716
|
+
res, p0 = self._run_least_squares(**kwargs)
|
|
717
|
+
except (RuntimeError, ValueError) as e:
|
|
718
|
+
# print("fitting failed", flush=True)
|
|
719
|
+
# raise
|
|
720
|
+
if return_exception:
|
|
721
|
+
return e
|
|
722
|
+
else:
|
|
723
|
+
raise
|
|
724
|
+
|
|
725
|
+
popt, pcov, psigma, all_chisq = self._calc_popt_pcov_psigma_chisq(res, p0)
|
|
726
|
+
|
|
727
|
+
self._popt = list(zip(self.argnames, popt))
|
|
728
|
+
self._psigma = list(zip(self.argnames, psigma))
|
|
729
|
+
self._pcov = pcov
|
|
730
|
+
self._chisq_dof = all_chisq
|
|
731
|
+
self._fit_result = res
|
|
732
|
+
|
|
733
|
+
self.build_TeX_info()
|
|
734
|
+
self.build_plotter()
|