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,980 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
r"""Spiral mesh plots and associated binning utilities."""
|
|
3
|
+
|
|
4
|
+
import pdb # noqa: F401
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import matplotlib as mpl
|
|
10
|
+
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from numbers import Number
|
|
13
|
+
from collections import namedtuple
|
|
14
|
+
from numba import njit, prange
|
|
15
|
+
|
|
16
|
+
from matplotlib import pyplot as plt
|
|
17
|
+
|
|
18
|
+
from . import base
|
|
19
|
+
from . import labels as labels_module
|
|
20
|
+
|
|
21
|
+
InitialSpiralEdges = namedtuple("InitialSpiralEdges", "x,y")
|
|
22
|
+
# SpiralMeshData = namedtuple("SpiralMeshData", "x,y")
|
|
23
|
+
SpiralMeshBinID = namedtuple("SpiralMeshBinID", "id,fill,visited")
|
|
24
|
+
SpiralFilterThresholds = namedtuple(
|
|
25
|
+
"SpiralFilterThresholds", "density,size", defaults=(False,)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@njit(parallel=True)
|
|
30
|
+
def get_counts_per_bin(bins, x, y):
|
|
31
|
+
nbins = bins.shape[0]
|
|
32
|
+
cell_count = np.full(nbins, 0, dtype=np.int64)
|
|
33
|
+
|
|
34
|
+
for i in prange(nbins):
|
|
35
|
+
x0, x1, y0, y1 = bins[i]
|
|
36
|
+
left = x >= x0
|
|
37
|
+
right = x < x1
|
|
38
|
+
bottom = y >= y0
|
|
39
|
+
top = y < y1
|
|
40
|
+
chk_cell = left & right & bottom & top
|
|
41
|
+
cell_count[i] = chk_cell.sum()
|
|
42
|
+
|
|
43
|
+
return cell_count
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@njit(parallel=True)
|
|
47
|
+
def calculate_bin_number_with_numba(mesh, x, y):
|
|
48
|
+
fill = -9999
|
|
49
|
+
zbin = np.full(x.size, fill, dtype=np.int64)
|
|
50
|
+
|
|
51
|
+
nbins = mesh.shape[0]
|
|
52
|
+
bin_visited = np.zeros(nbins, dtype=np.int64)
|
|
53
|
+
for i in prange(nbins):
|
|
54
|
+
x0, x1, y0, y1 = mesh[i]
|
|
55
|
+
|
|
56
|
+
# Assume that largest x- and y-edges are extended by larger of 1% and 0.01
|
|
57
|
+
# so that we can just naively use < instead of a special case of <=.
|
|
58
|
+
# At time of writing (20200418), `SpiralPlot.initialize_mesh` did this.
|
|
59
|
+
tk = (x >= x0) & (x < x1) & (y >= y0) & (y < y1)
|
|
60
|
+
|
|
61
|
+
zbin[tk] = i
|
|
62
|
+
bin_visited[i] += 1
|
|
63
|
+
|
|
64
|
+
return zbin, fill, bin_visited
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class SpiralMesh(object):
|
|
68
|
+
def __init__(self, x, y, initial_xedges, initial_yedges, min_per_bin=250):
|
|
69
|
+
self.set_data(x, y)
|
|
70
|
+
self.set_min_per_bin(min_per_bin)
|
|
71
|
+
self.set_initial_edges(initial_xedges, initial_yedges)
|
|
72
|
+
self._cell_filter_thresholds = SpiralFilterThresholds(density=False, size=False)
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def bin_id(self):
|
|
76
|
+
return self._bin_id
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def cat(self):
|
|
80
|
+
r""":py:class:`pd.Categorical` version of `bin_id`, with fill bin removed."""
|
|
81
|
+
return self._cat
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def data(self):
|
|
85
|
+
return self._data
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def initial_edges(self):
|
|
89
|
+
return self._initial_edges
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def mesh(self):
|
|
93
|
+
return self._mesh
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def min_per_bin(self):
|
|
97
|
+
return self._min_per_bin
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def cell_filter_thresholds(self):
|
|
101
|
+
return self._cell_filter_thresholds
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def cell_filter(self):
|
|
105
|
+
r"""Boolean :py:class:`Series` identifying properly filled mesh cells.
|
|
106
|
+
|
|
107
|
+
Series selects mesh cells that meet density and area criteria specified
|
|
108
|
+
by :py:meth:`mesh_cell_filter_thresholds`.
|
|
109
|
+
|
|
110
|
+
Notes
|
|
111
|
+
----
|
|
112
|
+
Neither `density` nor `size` convert log-scale edges into linear scale.
|
|
113
|
+
Doing so would overweight the area of mesh cells at larger values on a
|
|
114
|
+
given axis.
|
|
115
|
+
"""
|
|
116
|
+
density = self.cell_filter_thresholds.density
|
|
117
|
+
size = self.cell_filter_thresholds.size
|
|
118
|
+
|
|
119
|
+
x = self.mesh[:, [0, 1]]
|
|
120
|
+
y = self.mesh[:, [2, 3]]
|
|
121
|
+
|
|
122
|
+
dx = x[:, 1] - x[:, 0]
|
|
123
|
+
dy = y[:, 1] - y[:, 0]
|
|
124
|
+
dA = dx * dy
|
|
125
|
+
|
|
126
|
+
tk = np.full_like(dx, True, dtype=bool)
|
|
127
|
+
if size:
|
|
128
|
+
size_quantile = np.quantile(dA, size)
|
|
129
|
+
tk_size = dA < size_quantile
|
|
130
|
+
tk = tk & (tk_size)
|
|
131
|
+
if density:
|
|
132
|
+
cnt = np.bincount(self.bin_id.id, minlength=self.mesh.shape[0])
|
|
133
|
+
assert cnt.shape == tk.shape
|
|
134
|
+
cell_density = cnt / dA
|
|
135
|
+
density_quantile = np.quantile(cell_density, density)
|
|
136
|
+
tk_density = cell_density > density_quantile
|
|
137
|
+
tk = tk & tk_density
|
|
138
|
+
|
|
139
|
+
return tk
|
|
140
|
+
|
|
141
|
+
def set_cell_filter_thresholds(self, **kwargs):
|
|
142
|
+
r"""Set or update the :py:meth:`mesh_cell_filter_thresholds`.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
density: scalar
|
|
147
|
+
The density quantile above which we want to select bins, e.g.
|
|
148
|
+
above the 0.01 quantile. This ensures that each bin meets some
|
|
149
|
+
sufficient fill factor.
|
|
150
|
+
size: scalar
|
|
151
|
+
The size quantile below which we want to select bins, e.g.
|
|
152
|
+
below the 0.99 quantile. This ensures that the bin isn't so large
|
|
153
|
+
that it will appear as an outlier.
|
|
154
|
+
"""
|
|
155
|
+
density = kwargs.pop("density", False)
|
|
156
|
+
size = kwargs.pop("size", False)
|
|
157
|
+
if len(kwargs.keys()):
|
|
158
|
+
extra = "\n".join(["{}: {}".format(k, v) for k, v in kwargs.items()])
|
|
159
|
+
raise KeyError("Unexpected kwarg\n{}".format(extra))
|
|
160
|
+
|
|
161
|
+
self._cell_filter_thresholds = SpiralFilterThresholds(
|
|
162
|
+
density=density, size=size
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def set_initial_edges(self, xedges, yedges):
|
|
166
|
+
self._initial_edges = InitialSpiralEdges(xedges, yedges)
|
|
167
|
+
|
|
168
|
+
def set_data(self, x, y):
|
|
169
|
+
data = pd.concat({"x": x, "y": y}, axis=1)
|
|
170
|
+
self._data = data # SpiralMeshData(x, y)
|
|
171
|
+
|
|
172
|
+
def set_min_per_bin(self, new):
|
|
173
|
+
self._min_per_bin = int(new)
|
|
174
|
+
|
|
175
|
+
def initialize_bins(self):
|
|
176
|
+
# Leaves initial edges altered when we change maximum edge.
|
|
177
|
+
xbins = self.initial_edges.x
|
|
178
|
+
ybins = self.initial_edges.y
|
|
179
|
+
|
|
180
|
+
# # Account for highest bin = 0 already done in `SpiralPlot2D.initialize_mesh`.
|
|
181
|
+
# xbins[-1] = np.max([0.01, 1.01 * xbins[-1]])
|
|
182
|
+
# ybins[-1] = np.max([0.01, 1.01 * ybins[-1]])
|
|
183
|
+
|
|
184
|
+
left = xbins[:-1]
|
|
185
|
+
right = xbins[1:]
|
|
186
|
+
bottom = ybins[:-1]
|
|
187
|
+
top = ybins[1:]
|
|
188
|
+
|
|
189
|
+
nx = left.size
|
|
190
|
+
ny = bottom.size
|
|
191
|
+
|
|
192
|
+
mesh = np.full((nx * ny, 4), np.nan, dtype=np.float64)
|
|
193
|
+
for x0, x1, i in zip(left, right, range(nx)):
|
|
194
|
+
for y0, y1, j in zip(bottom, top, range(ny)):
|
|
195
|
+
# NOTE: i*ny+j means go to i'th row, which has
|
|
196
|
+
# nrow * number of bins passed. Then go
|
|
197
|
+
# to j'th bin because we have to traverse
|
|
198
|
+
# to the j'th y-bin too.
|
|
199
|
+
mesh[(i * ny) + j] = [x0, x1, y0, y1]
|
|
200
|
+
|
|
201
|
+
mesh = np.array(mesh)
|
|
202
|
+
|
|
203
|
+
# pdb.set_trace()
|
|
204
|
+
|
|
205
|
+
self.initial_mesh = np.array(mesh)
|
|
206
|
+
return mesh
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def process_one_spiral_step(bins, x, y, min_per_bin):
|
|
210
|
+
# print("Processing spiral step", flush=True)
|
|
211
|
+
# start0 = datetime.now()
|
|
212
|
+
cell_count = get_counts_per_bin(bins, x, y)
|
|
213
|
+
|
|
214
|
+
bins_to_replace = cell_count > min_per_bin
|
|
215
|
+
nbins_to_replace = bins_to_replace.sum()
|
|
216
|
+
|
|
217
|
+
if not nbins_to_replace:
|
|
218
|
+
return None, 0
|
|
219
|
+
|
|
220
|
+
xhyh = 0.5 * (bins[:, [0, 2]] + bins[:, [1, 3]])
|
|
221
|
+
|
|
222
|
+
def split_this_cell(idx):
|
|
223
|
+
x0, x1, y0, y1 = bins[idx]
|
|
224
|
+
xh, yh = xhyh[idx]
|
|
225
|
+
|
|
226
|
+
# Reduce calls to `np.array`.
|
|
227
|
+
# Just return a list here.
|
|
228
|
+
split_cell = [
|
|
229
|
+
[x0, xh, y0, yh],
|
|
230
|
+
[xh, x1, y0, yh],
|
|
231
|
+
[xh, x1, yh, y1],
|
|
232
|
+
[x0, xh, yh, y1],
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
return split_cell
|
|
236
|
+
|
|
237
|
+
new_cells = bins_to_replace.sum() * [None]
|
|
238
|
+
for i, idx in enumerate(np.where(bins_to_replace)[0]):
|
|
239
|
+
new_cells[i] = split_this_cell(idx)
|
|
240
|
+
|
|
241
|
+
new_cells = np.vstack(new_cells)
|
|
242
|
+
|
|
243
|
+
bins[bins_to_replace] = np.nan
|
|
244
|
+
|
|
245
|
+
# stop = datetime.now()
|
|
246
|
+
# print(f"Done Building replacement grid cells (dt={stop-start1})", flush=True)
|
|
247
|
+
# print(f"Done Processing spiral step (dt={stop-start0})", flush=True)
|
|
248
|
+
|
|
249
|
+
return new_cells, nbins_to_replace
|
|
250
|
+
|
|
251
|
+
@staticmethod
|
|
252
|
+
def _visualize_logged_stats(stats_str):
|
|
253
|
+
from matplotlib import pyplot as plt
|
|
254
|
+
|
|
255
|
+
stats = [[y.strip() for y in x.split(" ") if y] for x in stats_str.split("\n")]
|
|
256
|
+
stats.pop(1) # Remove column underline row
|
|
257
|
+
stats = np.array(stats)
|
|
258
|
+
index = pd.Index(stats[1:, 0].astype(int), name="Step")
|
|
259
|
+
n_replaced = stats[1:, 1].astype(int)
|
|
260
|
+
|
|
261
|
+
dt = pd.to_timedelta(stats[1:, 2]).total_seconds()
|
|
262
|
+
dt_unit = "s"
|
|
263
|
+
if dt.max() > 60:
|
|
264
|
+
dt /= 60
|
|
265
|
+
dt_unit = "m"
|
|
266
|
+
if dt.max() > 60:
|
|
267
|
+
dt /= 60
|
|
268
|
+
dt_unit = "H"
|
|
269
|
+
if dt.max() > 24:
|
|
270
|
+
dt /= 24
|
|
271
|
+
dt_unit = "D"
|
|
272
|
+
|
|
273
|
+
dt_key = f"Elapsed [{dt_unit}]"
|
|
274
|
+
stats = pd.DataFrame({dt_key: dt, "N Divisions": n_replaced}, index=index)
|
|
275
|
+
|
|
276
|
+
# stats = pd.Series(stats[1:, 1].astype(int), index=stats[1:, 0].astype(int), name=stats[0, 1])
|
|
277
|
+
# stats.index.name = stats[0, 0]
|
|
278
|
+
|
|
279
|
+
fig, ax = plt.subplots()
|
|
280
|
+
tax = ax.twinx()
|
|
281
|
+
|
|
282
|
+
x = stats.index
|
|
283
|
+
k = f"Elapsed [{dt_unit}]"
|
|
284
|
+
ax.plot(x, stats.loc[:, k], label=k, marker="+", ms=8)
|
|
285
|
+
|
|
286
|
+
k = "N Divisions"
|
|
287
|
+
tax.plot(x, stats.loc[:, k], label=k, c="C1", ls="--", marker="x", ms=8)
|
|
288
|
+
|
|
289
|
+
tax.grid(False)
|
|
290
|
+
ax.set_xlabel("Step Number")
|
|
291
|
+
ax.set_ylabel(dt_key)
|
|
292
|
+
tax.set_ylabel("N Divisions")
|
|
293
|
+
|
|
294
|
+
h0, l0 = ax.get_legend_handles_labels()
|
|
295
|
+
h1, l1 = tax.get_legend_handles_labels()
|
|
296
|
+
|
|
297
|
+
ax.legend(
|
|
298
|
+
h0 + h1,
|
|
299
|
+
l0 + l1,
|
|
300
|
+
title=rf"$\Delta t = {stats.loc[:, dt_key].sum():.0f} \, {dt_unit}$",
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
ax.set_yscale("log")
|
|
304
|
+
tax.set_yscale("log")
|
|
305
|
+
|
|
306
|
+
return ax, tax, stats
|
|
307
|
+
|
|
308
|
+
def generate_mesh(self):
|
|
309
|
+
logger = logging.getLogger("__main__")
|
|
310
|
+
start = datetime.now()
|
|
311
|
+
logger.warning(f"Generating {self.__class__.__name__} at {start}")
|
|
312
|
+
|
|
313
|
+
x = self.data.x.values
|
|
314
|
+
y = self.data.y.values
|
|
315
|
+
|
|
316
|
+
min_per_bin = self.min_per_bin
|
|
317
|
+
# max_bins = int(1e5)
|
|
318
|
+
|
|
319
|
+
initial_bins = self.initialize_bins()
|
|
320
|
+
|
|
321
|
+
# To reduce memory needs, only process data in mesh.
|
|
322
|
+
x0 = initial_bins[:, 0].min()
|
|
323
|
+
x1 = initial_bins[:, 1].max()
|
|
324
|
+
y0 = initial_bins[:, 2].min()
|
|
325
|
+
y1 = initial_bins[:, 3].max()
|
|
326
|
+
tk_data_in_mesh = (
|
|
327
|
+
(x0 <= x)
|
|
328
|
+
& (x <= x1)
|
|
329
|
+
& (y0 <= y)
|
|
330
|
+
& (y <= y1)
|
|
331
|
+
& np.isfinite(x)
|
|
332
|
+
& np.isfinite(y)
|
|
333
|
+
)
|
|
334
|
+
x = x[tk_data_in_mesh]
|
|
335
|
+
y = y[tk_data_in_mesh]
|
|
336
|
+
|
|
337
|
+
initial_cell_count = get_counts_per_bin(initial_bins, x, y)
|
|
338
|
+
# initial_cell_count = self.get_counts_per_bin_loop(initial_bins, x, y)
|
|
339
|
+
bins_to_replace = initial_cell_count > min_per_bin
|
|
340
|
+
nbins_to_replace = bins_to_replace.sum()
|
|
341
|
+
|
|
342
|
+
# raise ValueError
|
|
343
|
+
|
|
344
|
+
list_of_bins = [initial_bins]
|
|
345
|
+
active_bins = initial_bins
|
|
346
|
+
|
|
347
|
+
logger.warning(
|
|
348
|
+
"""
|
|
349
|
+
Step N Elapsed Time
|
|
350
|
+
====== ======= =============="""
|
|
351
|
+
)
|
|
352
|
+
step_start = datetime.now()
|
|
353
|
+
step = 0
|
|
354
|
+
while nbins_to_replace > 0:
|
|
355
|
+
active_bins, nbins_to_replace = self.process_one_spiral_step(
|
|
356
|
+
active_bins, x, y, min_per_bin
|
|
357
|
+
)
|
|
358
|
+
now = datetime.now()
|
|
359
|
+
# if not(step % 10):
|
|
360
|
+
logger.warning(f"{step:>6} {nbins_to_replace:>7} {(now - step_start)}")
|
|
361
|
+
list_of_bins.append(active_bins)
|
|
362
|
+
step += 1
|
|
363
|
+
step_start = now
|
|
364
|
+
|
|
365
|
+
list_of_bins = [b for b in list_of_bins if b is not None]
|
|
366
|
+
final_bins = np.vstack(list_of_bins)
|
|
367
|
+
valid_bins = np.isfinite(final_bins).all(axis=1)
|
|
368
|
+
final_bins = final_bins[valid_bins]
|
|
369
|
+
|
|
370
|
+
stop = datetime.now()
|
|
371
|
+
# logger.warning(f"Complete at {stop}")
|
|
372
|
+
logger.warning(f"\nCompleted {self.__class__.__name__} at {stop}")
|
|
373
|
+
logger.warning(f"Elasped time {stop - start}")
|
|
374
|
+
logger.warning(f"Split bin threshold {min_per_bin}")
|
|
375
|
+
logger.warning(
|
|
376
|
+
f"Generated {final_bins.shape[0]} bins for {x.size} spectra (~{x.size / final_bins.shape[0]:.3f} spectra per bin)\n"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
self._mesh = final_bins
|
|
380
|
+
|
|
381
|
+
# return final_bins
|
|
382
|
+
|
|
383
|
+
def calculate_bin_number(self):
|
|
384
|
+
logger = logging.getLogger(__name__)
|
|
385
|
+
logger.warning(
|
|
386
|
+
f"Calculating {self.__class__.__name__} bin_number at {datetime.now()}"
|
|
387
|
+
)
|
|
388
|
+
x = self.data.loc[:, "x"].values
|
|
389
|
+
y = self.data.loc[:, "y"].values
|
|
390
|
+
mesh = self.mesh
|
|
391
|
+
nbins = mesh.shape[0]
|
|
392
|
+
|
|
393
|
+
start = datetime.now()
|
|
394
|
+
zbin, fill, bin_visited = calculate_bin_number_with_numba(mesh, x, y)
|
|
395
|
+
stop = datetime.now()
|
|
396
|
+
|
|
397
|
+
logger.warning(f"Elapsed time {stop - start}")
|
|
398
|
+
|
|
399
|
+
# return calculate_bin_number_with_numba_broadcast(mesh, x, y, fill)
|
|
400
|
+
|
|
401
|
+
# if ( verbose > 0 and
|
|
402
|
+
# (i % verbose == 0) ):
|
|
403
|
+
# print(i+1, end=", ")
|
|
404
|
+
|
|
405
|
+
if (zbin == fill).any():
|
|
406
|
+
# if (zbin < 0).any():
|
|
407
|
+
# pdb.set_trace()
|
|
408
|
+
logger.warning(
|
|
409
|
+
f"""`zbin` contains {(zbin == fill).sum()} ({100 * (zbin == fill).mean():.1f}%) fill values that are outside of mesh.
|
|
410
|
+
They will be replaced by NaNs and excluded from the aggregation.
|
|
411
|
+
"""
|
|
412
|
+
)
|
|
413
|
+
# raise ValueError(msg % (zbin == fill).sum())
|
|
414
|
+
|
|
415
|
+
# Set fill bin to zero
|
|
416
|
+
is_fill = zbin == fill
|
|
417
|
+
# zbin[~is_fill] += 1
|
|
418
|
+
# zbin[is_fill] = -1
|
|
419
|
+
# print(zbin.min())
|
|
420
|
+
# zbin += 1
|
|
421
|
+
# print(zbin.min())
|
|
422
|
+
# `minlength=nbins` forces us to include empty bins at the end of the array.
|
|
423
|
+
bin_frequency = np.bincount(zbin[~is_fill], minlength=nbins)
|
|
424
|
+
n_empty = (bin_frequency == 0).sum()
|
|
425
|
+
logger.warning(
|
|
426
|
+
f"""Largest bin population is {bin_frequency.max()}
|
|
427
|
+
{n_empty} of {nbins} bins ({100 * n_empty / nbins:.1f}%) are empty
|
|
428
|
+
"""
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
if not bin_visited.all():
|
|
432
|
+
logger.warning(f"{(~bin_visited).sum()} bins went unvisited.")
|
|
433
|
+
if (bin_visited > 1).any():
|
|
434
|
+
logger.warning(f"({(bin_visited > 1).sum()} bins visted more than once.")
|
|
435
|
+
|
|
436
|
+
if nbins - bin_frequency.shape[0] != 0:
|
|
437
|
+
raise ValueError(
|
|
438
|
+
f"{nbins - bin_frequency.shape[0]} mesh cells do not have an associated z-value"
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
# zbin = _pd.Series(zbin, index=self.data.index, name="zbin")
|
|
442
|
+
# # Pandas groupby will treat NaN as not belonging to a bin.
|
|
443
|
+
# zbin.replace(fill, _np.nan, inplace=True)
|
|
444
|
+
bin_id = SpiralMeshBinID(zbin, fill, bin_visited)
|
|
445
|
+
self._bin_id = bin_id
|
|
446
|
+
return bin_id
|
|
447
|
+
|
|
448
|
+
def place_spectra_in_mesh(self):
|
|
449
|
+
self.generate_mesh()
|
|
450
|
+
bin_id = self.calculate_bin_number()
|
|
451
|
+
return bin_id
|
|
452
|
+
|
|
453
|
+
def build_cat(self):
|
|
454
|
+
bin_id = self.bin_id.id
|
|
455
|
+
fill = self.bin_id.fill
|
|
456
|
+
|
|
457
|
+
# Integer number corresponds to the order over
|
|
458
|
+
# which the mesh was traversed.
|
|
459
|
+
cat = pd.Categorical(bin_id, ordered=False)
|
|
460
|
+
if fill in bin_id:
|
|
461
|
+
cat.remove_categories(fill, inplace=True)
|
|
462
|
+
|
|
463
|
+
self._cat = cat
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
class SpiralPlot2D(base.PlotWithZdata, base.CbarMaker):
|
|
467
|
+
r"""2D spiral plotting with adaptive mesh refinement.
|
|
468
|
+
|
|
469
|
+
Examples
|
|
470
|
+
--------
|
|
471
|
+
splot = SpiralPlot2D(...)
|
|
472
|
+
splot.initialize_mesh()
|
|
473
|
+
"""
|
|
474
|
+
|
|
475
|
+
def __init__(
|
|
476
|
+
self, x, y, z=None, logx=False, logy=False, initial_bins=5, clip_data=False
|
|
477
|
+
):
|
|
478
|
+
super().__init__()
|
|
479
|
+
self.set_log(x=logx, y=logy)
|
|
480
|
+
self.set_data(x, y, z, clip_data)
|
|
481
|
+
self.set_labels(x="x", y="y", z=labels_module.Count() if z is None else "z")
|
|
482
|
+
self.calc_initial_bins(initial_bins)
|
|
483
|
+
self.set_clim(None, None)
|
|
484
|
+
|
|
485
|
+
@property
|
|
486
|
+
def clim(self):
|
|
487
|
+
return self._clim
|
|
488
|
+
|
|
489
|
+
@property
|
|
490
|
+
def initial_bins(self):
|
|
491
|
+
return dict(self._initial_bins)
|
|
492
|
+
|
|
493
|
+
@property
|
|
494
|
+
def grouped(self):
|
|
495
|
+
return self._grouped
|
|
496
|
+
|
|
497
|
+
@property
|
|
498
|
+
def mesh(self):
|
|
499
|
+
return self._mesh
|
|
500
|
+
|
|
501
|
+
def agg(self, fcn=None):
|
|
502
|
+
r"""Aggregate the z-values into their bins."""
|
|
503
|
+
self.logger.debug("aggregating z-data")
|
|
504
|
+
|
|
505
|
+
# start = datetime.now()
|
|
506
|
+
# self.logger.warning(f"Start {start}")
|
|
507
|
+
|
|
508
|
+
if fcn is None:
|
|
509
|
+
if self.data.loc[:, "z"].unique().size == 1:
|
|
510
|
+
fcn = "count"
|
|
511
|
+
else:
|
|
512
|
+
fcn = "mean"
|
|
513
|
+
|
|
514
|
+
gb = self.grouped
|
|
515
|
+
agg = gb.agg(fcn)
|
|
516
|
+
|
|
517
|
+
c0, c1 = self.clim
|
|
518
|
+
if c0 is not None or c1 is not None:
|
|
519
|
+
cnt = gb.agg("count")
|
|
520
|
+
tk = pd.Series(True, index=agg.index)
|
|
521
|
+
|
|
522
|
+
if c0 is not None:
|
|
523
|
+
tk = tk & (cnt >= c0)
|
|
524
|
+
if c1 is not None:
|
|
525
|
+
tk = tk & (cnt <= c1)
|
|
526
|
+
|
|
527
|
+
agg = agg.where(tk)
|
|
528
|
+
|
|
529
|
+
# reindex to ensure we have a z-value for every bin.
|
|
530
|
+
reindex = pd.RangeIndex(start=0, stop=self.mesh.mesh.shape[0], step=1)
|
|
531
|
+
agg = agg.reindex(reindex)
|
|
532
|
+
|
|
533
|
+
cell_filter = self.mesh.cell_filter
|
|
534
|
+
if agg.shape != cell_filter.shape:
|
|
535
|
+
raise ValueError(
|
|
536
|
+
f"""Unable to algin `agg` and `cell_filter.
|
|
537
|
+
agg : {agg.shape}
|
|
538
|
+
filter : {cell_filter.shape}"""
|
|
539
|
+
)
|
|
540
|
+
# pdb.set_trace()
|
|
541
|
+
agg = agg.where(cell_filter, axis=0)
|
|
542
|
+
|
|
543
|
+
# stop = datetime.now()
|
|
544
|
+
# self.logger.warning(f"Stop {stop}")
|
|
545
|
+
# self.logger.warning(f"Elapsed {stop - start}")
|
|
546
|
+
|
|
547
|
+
return agg
|
|
548
|
+
|
|
549
|
+
def build_grouped(self):
|
|
550
|
+
cat = self.mesh.cat
|
|
551
|
+
z = self.data.loc[:, "z"]
|
|
552
|
+
|
|
553
|
+
if not (cat.size == z.size):
|
|
554
|
+
raise ValueError(
|
|
555
|
+
f"""`cat` must have same size as data's first dimesion
|
|
556
|
+
cat : {cat.size}
|
|
557
|
+
data : {z.size}
|
|
558
|
+
"""
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
gb = z.groupby(cat)
|
|
562
|
+
self._grouped = gb
|
|
563
|
+
|
|
564
|
+
def calc_initial_bins(self, nbins):
|
|
565
|
+
data = self.data
|
|
566
|
+
keys = ("x", "y")
|
|
567
|
+
bins = {}
|
|
568
|
+
|
|
569
|
+
if isinstance(nbins, int):
|
|
570
|
+
# Single paramter for `nbins`.
|
|
571
|
+
nbins = {k: nbins for k in keys}
|
|
572
|
+
|
|
573
|
+
elif len(nbins) == len(keys):
|
|
574
|
+
# Passed one bin spec per axis
|
|
575
|
+
nbins = {k: v for k, v in zip(keys, nbins)}
|
|
576
|
+
|
|
577
|
+
else:
|
|
578
|
+
msg = f"Unrecognized `nbins`\ntype: {type(nbins)}\n bins:{nbins}"
|
|
579
|
+
raise ValueError(msg)
|
|
580
|
+
|
|
581
|
+
for k, b in nbins.items():
|
|
582
|
+
# Numpy and Astropy don't like NaNs when calculating bins.
|
|
583
|
+
# Infinities in bins (typically from log10(0)) also create problems.
|
|
584
|
+
d = data.loc[:, k].replace([-np.inf, np.inf], np.nan).dropna()
|
|
585
|
+
|
|
586
|
+
if not isinstance(b, (int, np.ndarray)):
|
|
587
|
+
raise TypeError("Only want in integer or np.ndarrays for initial edges")
|
|
588
|
+
|
|
589
|
+
if isinstance(b, int):
|
|
590
|
+
# Lets calculate the following quantiles.
|
|
591
|
+
b = np.quantile(
|
|
592
|
+
d, np.linspace(0, 1, b + 1)
|
|
593
|
+
) # Need N + 1 edges to make N bins.
|
|
594
|
+
|
|
595
|
+
# Extend the right most bin by the larger of 1% or 0.01 (in the case of zero)
|
|
596
|
+
# So that y < y1 inludes data at real data edge.
|
|
597
|
+
b[-1] = np.max([0.01, 1.01 * b.max()])
|
|
598
|
+
|
|
599
|
+
assert not np.isnan(b).any()
|
|
600
|
+
|
|
601
|
+
bins[k] = b
|
|
602
|
+
|
|
603
|
+
bins = tuple(bins.items())
|
|
604
|
+
self._initial_bins = bins
|
|
605
|
+
return bins
|
|
606
|
+
|
|
607
|
+
def initialize_mesh(self, **kwargs):
|
|
608
|
+
x = self.data.loc[:, "x"]
|
|
609
|
+
y = self.data.loc[:, "y"]
|
|
610
|
+
|
|
611
|
+
# if self.log.x:
|
|
612
|
+
# x = x.apply(np.log10)
|
|
613
|
+
# if self.log.y:
|
|
614
|
+
# y = y.apply(np.log10)
|
|
615
|
+
|
|
616
|
+
xbins = self.initial_bins["x"]
|
|
617
|
+
ybins = self.initial_bins["y"]
|
|
618
|
+
|
|
619
|
+
mesh = SpiralMesh(x, y, xbins, ybins, **kwargs)
|
|
620
|
+
# Attach mesh before anything else.
|
|
621
|
+
# Makes debugging easier.
|
|
622
|
+
self._mesh = mesh
|
|
623
|
+
|
|
624
|
+
mesh.place_spectra_in_mesh()
|
|
625
|
+
mesh.build_cat()
|
|
626
|
+
|
|
627
|
+
def set_clim(self, lower=None, upper=None):
|
|
628
|
+
"""Set the min (lower) and max (upper) counts per bin.
|
|
629
|
+
|
|
630
|
+
This limit is applied after the :py:meth:`groupby.agg` is run."""
|
|
631
|
+
assert isinstance(lower, Number) or lower is None
|
|
632
|
+
assert isinstance(upper, Number) or upper is None
|
|
633
|
+
self._clim = base.RangeLimits(lower, upper)
|
|
634
|
+
|
|
635
|
+
def set_data(self, x, y, z, clip):
|
|
636
|
+
super().set_data(x, y, z, clip)
|
|
637
|
+
data = self.data
|
|
638
|
+
if self.log.x:
|
|
639
|
+
data.loc[:, "x"] = np.log10(np.abs(data.loc[:, "x"]))
|
|
640
|
+
if self.log.y:
|
|
641
|
+
data.loc[:, "y"] = np.log10(np.abs(data.loc[:, "y"]))
|
|
642
|
+
self._data = data
|
|
643
|
+
|
|
644
|
+
def _limit_color_norm(self, norm):
|
|
645
|
+
pct = self.data.loc[:, "z"].quantile([0.01, 0.99])
|
|
646
|
+
v0 = pct.loc[0.01]
|
|
647
|
+
v1 = pct.loc[0.99]
|
|
648
|
+
if norm.vmin is None:
|
|
649
|
+
norm.vmin = v0
|
|
650
|
+
if norm.vmax is None:
|
|
651
|
+
norm.vmax = v1
|
|
652
|
+
norm.clip = True
|
|
653
|
+
|
|
654
|
+
def make_plot(
|
|
655
|
+
self,
|
|
656
|
+
ax=None,
|
|
657
|
+
cbar=True,
|
|
658
|
+
limit_color_norm=False,
|
|
659
|
+
cbar_kwargs=None,
|
|
660
|
+
fcn=None,
|
|
661
|
+
alpha_fcn=None,
|
|
662
|
+
**kwargs,
|
|
663
|
+
):
|
|
664
|
+
|
|
665
|
+
# start = datetime.now()
|
|
666
|
+
# self.logger.warning("Making plot")
|
|
667
|
+
# self.logger.warning(f"Start {start}")
|
|
668
|
+
|
|
669
|
+
if ax is None:
|
|
670
|
+
fig, ax = plt.subplots()
|
|
671
|
+
|
|
672
|
+
C = self.agg(fcn=fcn)
|
|
673
|
+
C = np.ma.masked_invalid(C.values)
|
|
674
|
+
assert isinstance(C, np.ndarray)
|
|
675
|
+
assert C.ndim == 1
|
|
676
|
+
if C.shape[0] != self.mesh.mesh.shape[0]:
|
|
677
|
+
raise ValueError(
|
|
678
|
+
f"""{self.mesh.mesh.shape[0] - C.shape[0]} mesh cells do not have a z-value associated with them. The z-values and mesh are not properly aligned."""
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
xmesh = self.mesh.mesh[:, [0, 1]]
|
|
682
|
+
ymesh = self.mesh.mesh[:, [2, 3]]
|
|
683
|
+
|
|
684
|
+
if self.log.x:
|
|
685
|
+
xmesh = 10.0**xmesh
|
|
686
|
+
if self.log.y:
|
|
687
|
+
ymesh = 10.0**ymesh
|
|
688
|
+
|
|
689
|
+
# (x,y) of bin's lower left corner.
|
|
690
|
+
xy = zip(xmesh[:, 0], ymesh[:, 0])
|
|
691
|
+
dx = xmesh[:, 1] - xmesh[:, 0]
|
|
692
|
+
dy = ymesh[:, 1] - ymesh[:, 0]
|
|
693
|
+
|
|
694
|
+
start1 = datetime.now()
|
|
695
|
+
self.logger.warning("Making patches")
|
|
696
|
+
self.logger.warning(f"Start {start1}")
|
|
697
|
+
|
|
698
|
+
patches = [
|
|
699
|
+
mpl.patches.Rectangle(this_xy, this_dx, this_dy)
|
|
700
|
+
for this_xy, this_dx, this_dy in zip(xy, dx, dy)
|
|
701
|
+
]
|
|
702
|
+
|
|
703
|
+
stop1 = datetime.now()
|
|
704
|
+
self.logger.warning(f"Stop {stop1}")
|
|
705
|
+
self.logger.warning(f"Elapsed {stop1 - start1}")
|
|
706
|
+
|
|
707
|
+
# TODO: `match_original=False` if calculate alpha for each patch.
|
|
708
|
+
edgecolors = "none"
|
|
709
|
+
linewidth = 0.0
|
|
710
|
+
collection = mpl.collections.PatchCollection(
|
|
711
|
+
patches, linewidth=linewidth, edgecolors=edgecolors
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
collection.set_array(C)
|
|
715
|
+
|
|
716
|
+
cmap = kwargs.pop("cmap", None)
|
|
717
|
+
norm = kwargs.pop("norm", None)
|
|
718
|
+
if len(kwargs):
|
|
719
|
+
raise ValueError(f"Unexpected kwargs {kwargs.keys()}")
|
|
720
|
+
# assert not kwargs
|
|
721
|
+
|
|
722
|
+
if limit_color_norm and norm is not None:
|
|
723
|
+
self._limit_color_norm(norm)
|
|
724
|
+
|
|
725
|
+
collection.set_alpha(None)
|
|
726
|
+
collection.set_cmap(cmap)
|
|
727
|
+
collection.set_norm(norm)
|
|
728
|
+
collection.autoscale_None()
|
|
729
|
+
|
|
730
|
+
ax.add_collection(collection, autolim=False)
|
|
731
|
+
|
|
732
|
+
minx = xmesh[:, 0].min()
|
|
733
|
+
miny = ymesh[:, 0].min()
|
|
734
|
+
maxx = xmesh[:, 1].max()
|
|
735
|
+
maxy = ymesh[:, 1].max()
|
|
736
|
+
collection.sticky_edges.x[:] = [minx, maxx]
|
|
737
|
+
collection.sticky_edges.y[:] = [miny, maxy]
|
|
738
|
+
corners = (minx, miny), (maxx, maxy)
|
|
739
|
+
ax.update_datalim(corners)
|
|
740
|
+
ax.autoscale_view()
|
|
741
|
+
|
|
742
|
+
cbar_or_mappable = collection
|
|
743
|
+
if cbar:
|
|
744
|
+
if cbar_kwargs is None:
|
|
745
|
+
cbar_kwargs = dict()
|
|
746
|
+
|
|
747
|
+
if "cax" not in cbar_kwargs.keys() and "ax" not in cbar_kwargs.keys():
|
|
748
|
+
cbar_kwargs["ax"] = ax
|
|
749
|
+
|
|
750
|
+
cbar = self._make_cbar(collection, norm=norm, **cbar_kwargs)
|
|
751
|
+
cbar_or_mappable = cbar
|
|
752
|
+
|
|
753
|
+
self._format_axis(ax)
|
|
754
|
+
|
|
755
|
+
if alpha_fcn is not None:
|
|
756
|
+
alpha_agg = np.ma.masked_invalid(self.agg(fcn=alpha_fcn).values)
|
|
757
|
+
# Feature scale then invert so smallest STD
|
|
758
|
+
# is most opaque.
|
|
759
|
+
alpha_agg = mpl.colors.Normalize()(alpha_agg)
|
|
760
|
+
alpha = 1 - alpha_agg
|
|
761
|
+
self.logger.warning("Scaling alpha filter as alpha**0.25")
|
|
762
|
+
alpha = alpha**0.25
|
|
763
|
+
|
|
764
|
+
# Set masked values to zero. Otherwise, masked
|
|
765
|
+
# values are rendered as black.
|
|
766
|
+
alpha = alpha.filled(0)
|
|
767
|
+
|
|
768
|
+
# Must draw to initialize `facecolor`s
|
|
769
|
+
plt.draw()
|
|
770
|
+
colors = collection.get_facecolors()
|
|
771
|
+
colors[:, 3] = alpha
|
|
772
|
+
collection.set_facecolor(colors)
|
|
773
|
+
|
|
774
|
+
# stop = datetime.now()
|
|
775
|
+
# self.logger.warning(f"Stop {stop}")
|
|
776
|
+
# self.logger.warning(f"Elapsed {stop - start}")
|
|
777
|
+
|
|
778
|
+
return ax, cbar_or_mappable
|
|
779
|
+
|
|
780
|
+
def _verify_contour_passthrough_kwargs(
|
|
781
|
+
self, ax, clabel_kwargs, edges_kwargs, cbar_kwargs
|
|
782
|
+
):
|
|
783
|
+
if clabel_kwargs is None:
|
|
784
|
+
clabel_kwargs = dict()
|
|
785
|
+
if edges_kwargs is None:
|
|
786
|
+
edges_kwargs = dict()
|
|
787
|
+
if cbar_kwargs is None:
|
|
788
|
+
cbar_kwargs = dict()
|
|
789
|
+
if "cax" not in cbar_kwargs.keys() and "ax" not in cbar_kwargs.keys():
|
|
790
|
+
cbar_kwargs["ax"] = ax
|
|
791
|
+
|
|
792
|
+
return clabel_kwargs, edges_kwargs, cbar_kwargs
|
|
793
|
+
|
|
794
|
+
def plot_contours(
|
|
795
|
+
self,
|
|
796
|
+
ax=None,
|
|
797
|
+
label_levels=True,
|
|
798
|
+
cbar=True,
|
|
799
|
+
limit_color_norm=False,
|
|
800
|
+
cbar_kwargs=None,
|
|
801
|
+
fcn=None,
|
|
802
|
+
plot_edges=False,
|
|
803
|
+
edges_kwargs=None,
|
|
804
|
+
clabel_kwargs=None,
|
|
805
|
+
skip_max_clbl=True,
|
|
806
|
+
use_contourf=False,
|
|
807
|
+
# gaussian_filter_std=0,
|
|
808
|
+
# gaussian_filter_kwargs=None,
|
|
809
|
+
**kwargs,
|
|
810
|
+
):
|
|
811
|
+
"""Make a contour plot on `ax` using `ax.contour`.
|
|
812
|
+
|
|
813
|
+
Parameters
|
|
814
|
+
----------
|
|
815
|
+
ax: mpl.axes.Axes, None
|
|
816
|
+
If None, create an `Axes` instance from `plt.subplots`.
|
|
817
|
+
label_levels: bool
|
|
818
|
+
If True, add labels to contours with `ax.clabel`.
|
|
819
|
+
cbar: bool
|
|
820
|
+
If True, create color bar with `labels.z`.
|
|
821
|
+
limit_color_norm: bool
|
|
822
|
+
If True, limit the color range to 0.001 and 0.999 percentile range
|
|
823
|
+
of the z-value, count or otherwise.
|
|
824
|
+
cbar_kwargs: dict, None
|
|
825
|
+
If not None, kwargs passed to `self._make_cbar`.
|
|
826
|
+
fcn: FunctionType, None
|
|
827
|
+
Aggregation function. If None, automatically select in :py:meth:`agg`.
|
|
828
|
+
plot_edges: bool
|
|
829
|
+
If True, plot the smoothed, extreme edges of the 2D histogram.
|
|
830
|
+
clabel_kwargs: None, dict
|
|
831
|
+
If not None, dictionary of kwargs passed to `ax.clabel`.
|
|
832
|
+
skip_max_clbl: bool
|
|
833
|
+
If True, don't label the maximum contour. Primarily used when the maximum
|
|
834
|
+
contour is, effectively, a point.
|
|
835
|
+
maximum_color:
|
|
836
|
+
The color for the maximum of the PDF.
|
|
837
|
+
use_contourf: bool
|
|
838
|
+
If True, use `ax.contourf`. Else use `ax.contour`.
|
|
839
|
+
gaussian_filter_std: int
|
|
840
|
+
If > 0, apply `scipy.ndimage.gaussian_filter` to the z-values using the
|
|
841
|
+
standard deviation specified by `gaussian_filter_std`.
|
|
842
|
+
gaussian_filter_kwargs: None, dict
|
|
843
|
+
If not None and gaussian_filter_std > 0, passed to :py:meth:`scipy.ndimage.gaussian_filter`
|
|
844
|
+
kwargs:
|
|
845
|
+
Passed to :py:meth:`ax.pcolormesh`.
|
|
846
|
+
If row or column normalized data, `norm` defaults to `mpl.colors.Normalize(0, 1)`.
|
|
847
|
+
"""
|
|
848
|
+
levels = kwargs.pop("levels", None)
|
|
849
|
+
cmap = kwargs.pop("cmap", None)
|
|
850
|
+
norm = kwargs.pop(
|
|
851
|
+
"norm",
|
|
852
|
+
None,
|
|
853
|
+
# mpl.colors.BoundaryNorm(np.linspace(0, 1, 11), 256, clip=True)
|
|
854
|
+
# if self.axnorm in ("c", "r")
|
|
855
|
+
# else None,
|
|
856
|
+
)
|
|
857
|
+
linestyles = kwargs.pop(
|
|
858
|
+
"linestyles",
|
|
859
|
+
[
|
|
860
|
+
"-",
|
|
861
|
+
":",
|
|
862
|
+
"--",
|
|
863
|
+
(0, (7, 3, 1, 3, 1, 3, 1, 3, 1, 3)),
|
|
864
|
+
"--",
|
|
865
|
+
":",
|
|
866
|
+
"-",
|
|
867
|
+
(0, (7, 3, 1, 3, 1, 3)),
|
|
868
|
+
],
|
|
869
|
+
)
|
|
870
|
+
|
|
871
|
+
if ax is None:
|
|
872
|
+
fig, ax = plt.subplots()
|
|
873
|
+
|
|
874
|
+
(
|
|
875
|
+
clabel_kwargs,
|
|
876
|
+
edges_kwargs,
|
|
877
|
+
cbar_kwargs,
|
|
878
|
+
) = self._verify_contour_passthrough_kwargs(
|
|
879
|
+
ax, clabel_kwargs, edges_kwargs, cbar_kwargs
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
inline = clabel_kwargs.pop("inline", True)
|
|
883
|
+
inline_spacing = clabel_kwargs.pop("inline_spacing", -3)
|
|
884
|
+
fmt = clabel_kwargs.pop("fmt", "%s")
|
|
885
|
+
|
|
886
|
+
if ax is None:
|
|
887
|
+
fig, ax = plt.subplots()
|
|
888
|
+
|
|
889
|
+
C = self.agg(fcn=fcn).values
|
|
890
|
+
assert isinstance(C, np.ndarray)
|
|
891
|
+
assert C.ndim == 1
|
|
892
|
+
if C.shape[0] != self.mesh.mesh.shape[0]:
|
|
893
|
+
raise ValueError(
|
|
894
|
+
f"""{self.mesh.mesh.shape[0] - C.shape[0]} mesh cells do not have a z-value associated with them. The z-values and mesh are not properly aligned."""
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
x = self.mesh.mesh[:, [0, 1]].mean(axis=1)
|
|
898
|
+
y = self.mesh.mesh[:, [2, 3]].mean(axis=1)
|
|
899
|
+
|
|
900
|
+
if self.log.x:
|
|
901
|
+
x = 10.0**x
|
|
902
|
+
if self.log.y:
|
|
903
|
+
y = 10.0**y
|
|
904
|
+
|
|
905
|
+
tk_finite = np.isfinite(C)
|
|
906
|
+
x = x[tk_finite]
|
|
907
|
+
y = y[tk_finite]
|
|
908
|
+
C = C[tk_finite]
|
|
909
|
+
|
|
910
|
+
contour_fcn = ax.tricontour
|
|
911
|
+
if use_contourf:
|
|
912
|
+
contour_fcn = ax.tricontourf
|
|
913
|
+
|
|
914
|
+
if levels is None:
|
|
915
|
+
args = [x, y, C]
|
|
916
|
+
else:
|
|
917
|
+
args = [x, y, C, levels]
|
|
918
|
+
|
|
919
|
+
qset = contour_fcn(*args, linestyles=linestyles, cmap=cmap, norm=norm, **kwargs)
|
|
920
|
+
|
|
921
|
+
try:
|
|
922
|
+
args = (qset, levels[:-1] if skip_max_clbl else levels)
|
|
923
|
+
except TypeError:
|
|
924
|
+
# None can't be subscripted.
|
|
925
|
+
args = (qset,)
|
|
926
|
+
|
|
927
|
+
class nf(float):
|
|
928
|
+
# Source: https://matplotlib.org/3.1.0/gallery/images_contours_and_fields/contour_label_demo.html
|
|
929
|
+
# Define a class that forces representation of float to look a certain way
|
|
930
|
+
# This remove trailing zero so '1.0' becomes '1'
|
|
931
|
+
def __repr__(self):
|
|
932
|
+
return str(self).rstrip("0")
|
|
933
|
+
|
|
934
|
+
lbls = None
|
|
935
|
+
if label_levels:
|
|
936
|
+
qset.levels = [nf(level) for level in qset.levels]
|
|
937
|
+
lbls = ax.clabel(
|
|
938
|
+
*args,
|
|
939
|
+
inline=inline,
|
|
940
|
+
inline_spacing=inline_spacing,
|
|
941
|
+
fmt=fmt,
|
|
942
|
+
**clabel_kwargs,
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
cbar_or_mappable = qset
|
|
946
|
+
if cbar:
|
|
947
|
+
# Pass `norm` to `self._make_cbar` so that we can choose the ticks to use.
|
|
948
|
+
cbar = self._make_cbar(qset, norm=norm, **cbar_kwargs)
|
|
949
|
+
cbar_or_mappable = cbar
|
|
950
|
+
|
|
951
|
+
self._format_axis(ax)
|
|
952
|
+
|
|
953
|
+
return ax, lbls, cbar_or_mappable, qset
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
# def plot_surface(self):
|
|
957
|
+
#
|
|
958
|
+
# from scipy.interpolate import griddata
|
|
959
|
+
#
|
|
960
|
+
# z = self.agg()
|
|
961
|
+
# x = self.mesh.mesh[:, [0, 1]].mean(axis=1)
|
|
962
|
+
# y = self.mesh.mesh[:, [2, 3]].mean(axis=1)
|
|
963
|
+
#
|
|
964
|
+
# is_finite = np.isfinite(z)
|
|
965
|
+
# z = z[is_finite]
|
|
966
|
+
# x = x[is_finite]
|
|
967
|
+
# y = y[is_finite]
|
|
968
|
+
#
|
|
969
|
+
# xi = np.linspace(x.min(), x.max(), 100)
|
|
970
|
+
# yi = np.linspace(y.min(), y.max(), 100)
|
|
971
|
+
# # VERY IMPORTANT, to tell matplotlib how is your data organized
|
|
972
|
+
# zi = griddata((x, y), y, (xi[None, :], yi[:, None]), method="cubic")
|
|
973
|
+
#
|
|
974
|
+
# if ax is None:
|
|
975
|
+
# fig = plt.figure(figsize=(8, 8))
|
|
976
|
+
# ax = fig.add_subplot(projection="3d")
|
|
977
|
+
#
|
|
978
|
+
# xig, yig = np.meshgrid(xi, yi)
|
|
979
|
+
#
|
|
980
|
+
# ax.plot_surface(xx, yy, zz, cmap="Spectral_r", norm=chavp.norms.vsw)
|