-
-
Notifications
You must be signed in to change notification settings - Fork 453
Ionization calculations #3070
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
andrewfullard
merged 59 commits into
tardis-sn:master
from
andrewfullard:photoion-rates-implementation
Aug 18, 2025
Merged
Ionization calculations #3070
Changes from all commits
Commits
Show all changes
59 commits
Select commit
Hold shift + click to select a range
348d7f1
Implementation of the rates matrix and level pop solvers into plasma
andrewfullard 1f8d852
Demonstration notebook
andrewfullard 1829a04
Vague ideas
andrewfullard bdfe800
Functional ion population solver
andrewfullard 0cecf6e
Add corrected coefficient solver
andrewfullard 9c61d53
Merge branch 'master' into photoion-rates-implementation
andrewfullard 1972152
Correct multiplication to compute strengths
andrewfullard 9b691f2
Simplify and fix various parts of the rate construction
andrewfullard e7da551
Update rate matrix construction
andrewfullard b9bd98e
Add recombination rates to matrix
andrewfullard 1da22ba
Handle charge conservation and matrix solving
andrewfullard 6251a6c
Add collisional rates to the matrix
andrewfullard 2f26e34
Correct recombination rate calculation
andrewfullard 4dba751
Iterative ion population solver
andrewfullard 69f3b48
Formatting
andrewfullard 85dc58e
Collisional rate index fix, ion solver iteration value
andrewfullard 49e6edf
Add demo notebook
andrewfullard 581371f
Basic collisional ionization strength tests
andrewfullard 408eb53
Improve estimator-based rate calculation
andrewfullard 94a039a
Very basic tests for collisional ionization rate class
andrewfullard b2bc887
Hydrogen continuum property
andrewfullard d43cb05
Output electron densities from the continuum solver
andrewfullard bda79ae
Merge branch 'master' into photoion-rates-implementation
andrewfullard ab29b5c
Some basic (failing) tests
andrewfullard 39fb72c
Hydrogen plasma additions
andrewfullard bdb62cf
Remove unnecessary lambda
andrewfullard 6847b1d
Batch of test fixes
andrewfullard 76d3ffd
Further test fixes
andrewfullard 8ac3c3b
Fix the rest of the tests
andrewfullard 21a07ac
Fix tests and improve/correct docstrings
andrewfullard c99bbdd
Update notebook
andrewfullard daa2a5b
Factor out reindexing utility
andrewfullard c0b45bc
Response to comments
andrewfullard e9f8c48
Replace DiluteBlackBodyContinuumPropertiesSolver with AnalyticPhotoio…
andrewfullard a5e752d
Correct property ordering
andrewfullard d47c7c7
Clarify population solution cuts
andrewfullard 9cef721
Remove unnecessary call
andrewfullard 9f029cc
Respond to comments
andrewfullard 1c61713
Major corrections to rate matrix construction and population solver
andrewfullard 125026b
Improvements to rate calculations
andrewfullard 3393573
Comparison notebook
andrewfullard 3332750
Add correct comparison notebook
andrewfullard 102c1b6
Fix calculation of level population ratio, handle Lyman continuum for…
andrewfullard 7cf6da3
Remove incorrect comparison notebook
andrewfullard e34efb9
Update tests to include boltzmann factor
andrewfullard f5eff36
Move solver tolerance to kwarg
andrewfullard 8cf1276
Store comparison plots
andrewfullard 5cc737e
Add some notes about iteration dependent quantities
andrewfullard 7de8bcc
Finalize ion population tests
andrewfullard 1b2c51a
Address Jack's comments
andrewfullard fe2577a
Merge branch 'master' into photoion-rates-implementation
andrewfullard 7e792f1
Cleanup, renames, comments with equations
andrewfullard a2ccdac
Fix excess unit conversion
andrewfullard 4cef991
Use fancy new sync dataframe
andrewfullard 8934011
Revert "Use fancy new sync dataframe"
andrewfullard fc3cdf5
Lock in docs notebooks
andrewfullard 677fc58
Use the real fancy new sync dataframe
andrewfullard 4d8a4ad
Run tests
andrewfullard 6fb6334
Fix test failure in old NLTE solver
andrewfullard File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
1,655 changes: 1,655 additions & 0 deletions
1,655
docs/physics/plasma/equilibrium/ionization_solution_comparison.ipynb
Large diffs are not rendered by default.
Oops, something went wrong.
821 changes: 821 additions & 0 deletions
821
docs/physics/plasma/equilibrium/ionization_solutions.ipynb
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,195 @@ | ||
| import logging | ||
|
|
||
| import astropy.units as u | ||
| import numpy as np | ||
| import pandas as pd | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| LOWER_ION_LEVEL_H = 0 | ||
|
|
||
|
|
||
| class IonPopulationSolver: | ||
| def __init__(self, rate_matrix_solver, max_solver_iterations=100): | ||
| """Solve the normalized ion population values from the rate matrices. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| rate_matrix_solver : IonRateMatrix | ||
| """ | ||
| self.rate_matrix_solver = rate_matrix_solver | ||
| self.max_solver_iterations = max_solver_iterations | ||
|
|
||
| def __calculate_balance_vector( | ||
| self, | ||
| elemental_number_density, | ||
| rate_matrix_index, | ||
| charge_conservation=False, | ||
| ): | ||
| """Constructs the balance vector for the NLTE ionization solver set of | ||
| equations by combining all solution vector blocks. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| elemental_number_density : pandas.Series | ||
| Elemental number densities indexed by atomic number. | ||
| rate_matrix_index : pandas.MultiIndex | ||
| (atomic_number, ion_number) | ||
| charge_conservation : bool, optional | ||
| Whether to include a charge conservation row. | ||
|
|
||
| Returns | ||
| ------- | ||
| numpy.array | ||
| Solution vector for the NLTE ionization solver. | ||
| """ | ||
| balance_array = [ | ||
| np.append( | ||
| np.zeros( | ||
| ( | ||
| rate_matrix_index.get_level_values("atomic_number") | ||
| == atomic_number | ||
| ).sum() | ||
| ), | ||
| elemental_number_density.loc[atomic_number], | ||
| ) | ||
| for atomic_number in elemental_number_density.index | ||
| ] | ||
|
|
||
| if charge_conservation: | ||
| balance_array.append(np.array([0.0])) | ||
|
|
||
| return np.hstack(balance_array) | ||
|
|
||
| def solve( | ||
| self, | ||
| radiation_field, | ||
| thermal_electron_energy_distribution, | ||
| elemental_number_density, | ||
| lte_level_population, | ||
| estimated_level_population, | ||
| lte_ion_population, | ||
| estimated_ion_population, | ||
| partition_function, | ||
| boltzmann_factor, | ||
| charge_conservation=False, | ||
| tolerance=1e-14, | ||
| ): | ||
| """Solves the normalized ion population values from the rate matrices. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| radiation_field : RadiationField | ||
| A radiation field that can compute its mean intensity. | ||
| thermal_electron_energy_distribution : ThermalElectronEnergyDistribution | ||
| Electron properties. | ||
| elemental_number_density : pd.DataFrame | ||
| Elemental number density. Index is atomic number, columns are cells. | ||
| lte_level_population : pd.DataFrame | ||
| LTE level number density. Columns are cells. | ||
| estimated_level_population : pd.DataFrame | ||
| Estimated level number density. Columns are cells. | ||
| lte_ion_population : pd.DataFrame | ||
| LTE ion number density. Columns are cells. | ||
| estimated_ion_population : pd.DataFrame | ||
| Estimated ion number density. Columns are cells. | ||
| charge_conservation : bool, optional | ||
| Whether to include a charge conservation row in the rate matrix. | ||
| tolerance : float, optional | ||
| Tolerance for convergence of the ion population solver. | ||
|
|
||
| Returns | ||
| ------- | ||
| pd.DataFrame | ||
| Normalized ion population values indexed by atomic number, ion | ||
| number and ion number. Columns are cells. | ||
| pd.DataFrame | ||
| Normalized electron fraction values. Columns are cells. | ||
| """ | ||
| # TODO: make more general indices that work for non-Hydrogen species | ||
| # this is the i level in Lucy 2003 | ||
| lower_ion_level_index = ( | ||
| lte_level_population.index.get_level_values("ion_number") | ||
| == LOWER_ION_LEVEL_H | ||
| ) | ||
|
|
||
| # this is the k level in Lucy 2003 | ||
| upper_ion_population_index = ( | ||
| lte_ion_population.index.get_level_values("ion_number") | ||
| > LOWER_ION_LEVEL_H | ||
| ) | ||
|
|
||
| new_electron_energy_distribution = thermal_electron_energy_distribution | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. perhaps a copy for debugging? If not useful, ignore this comment |
||
|
|
||
| for iteration in range(self.max_solver_iterations): | ||
| self.rates_matrices = self.rate_matrix_solver.solve( | ||
| radiation_field, | ||
| new_electron_energy_distribution, | ||
| lte_level_population.loc[lower_ion_level_index], | ||
| estimated_level_population.loc[lower_ion_level_index], | ||
| lte_ion_population.loc[upper_ion_population_index], | ||
| estimated_ion_population.loc[upper_ion_population_index], | ||
| partition_function, | ||
| boltzmann_factor, | ||
| charge_conservation, | ||
| ) | ||
| solved_matrices = pd.DataFrame( | ||
| index=self.rates_matrices.index, | ||
| columns=self.rates_matrices.columns, | ||
| ) | ||
| for cell in elemental_number_density.columns: | ||
| balance_vector = self.__calculate_balance_vector( | ||
| elemental_number_density[cell], | ||
| self.rates_matrices.index, | ||
| charge_conservation, | ||
| ) | ||
| solved_matrix = self.rates_matrices[cell].apply( | ||
| np.linalg.solve, args=(balance_vector,) | ||
| ) | ||
| solved_matrices[cell] = solved_matrix | ||
|
|
||
| ion_population_solution = pd.DataFrame( | ||
| np.vstack(solved_matrices.values[0]).T, | ||
| index=estimated_ion_population.index, | ||
| columns=self.rates_matrices.columns, | ||
| ) | ||
|
|
||
| if (ion_population_solution < 0).any().any(): | ||
| ion_population_solution[ion_population_solution < 0] = 0.0 | ||
|
|
||
| electron_population_solution = ( | ||
| ion_population_solution | ||
| * ion_population_solution.index.get_level_values("ion_number") | ||
| .values[np.newaxis] | ||
| .T | ||
| ).sum() | ||
|
|
||
| delta_ion = ( | ||
| estimated_ion_population - ion_population_solution | ||
| ) / ion_population_solution | ||
| delta_electron = ( | ||
| new_electron_energy_distribution.number_density.value | ||
| - electron_population_solution | ||
| ) / electron_population_solution | ||
|
|
||
| if ( | ||
andrewfullard marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| np.all(np.abs(delta_ion) < tolerance).any().any() | ||
| and (np.abs(delta_electron) < tolerance).any().any() | ||
| ): | ||
| logger.info( | ||
| "Ion population solver converged after %d iterations.", | ||
| iteration + 1, | ||
| ) | ||
| break | ||
|
|
||
| estimated_ion_population = ion_population_solution | ||
| new_electron_energy_distribution.number_density = ( | ||
| electron_population_solution.values * u.cm**-3 | ||
| ) | ||
| else: | ||
| logger.warning( | ||
| "Ion population solver did not converge after %d iterations.", | ||
| iteration, | ||
| ) | ||
|
|
||
| return ion_population_solution, electron_population_solution | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.