Skip to content

XRT DEM Iterative Solver Module#367

Open
joyvelasquez wants to merge 244 commits into
HinodeXRT:mainfrom
joyvelasquez:xray-plasma-decoder-dem-solver
Open

XRT DEM Iterative Solver Module#367
joyvelasquez wants to merge 244 commits into
HinodeXRT:mainfrom
joyvelasquez:xray-plasma-decoder-dem-solver

Conversation

@joyvelasquez

@joyvelasquez joyvelasquez commented Jul 16, 2025

Copy link
Copy Markdown
Contributor

Summary

This PR introduces XRTDEMIterative, a Python implementation of the IDL
routine xrt_dem_iterative2.pro for estimating Differential Emission
Measures (DEMs) from Hinode/XRT multi-filter observations.

What's Included

Core Solver (dem_solver.py)

  • XRTDEMIterative class with full DEM solving pipeline
  • Spline-parameterized DEM representation with iterative least-squares
    fitting via lmfit
  • Temperature response interpolation onto a regular log10(T) grid
  • User-supplied or default intensity uncertainty model:
    max(0.03 * intensity, 2 DN/s/pix), matching IDL default behavior
  • Monte Carlo uncertainty estimation via monte_carlo_runs parameter
  • Input validation with informative warnings (saturation, negative
    intensities, missing errors)
  • summary() method for diagnostic output

Plotting (dem_plotting.py)

  • plot_dem() — base DEM solution visualization
  • plot_dem_mc() — base DEM + Monte Carlo ensemble overlay

Tests

  • 70+ unit tests covering input validation, grid construction,
    response interpolation, spline system, residuals, solver, and
    Monte Carlo behavior
  • Currently - heavy work in progress (UPDATED JULY 2026)

Documentation

  • dem_overview.rst — introduction to DEM analysis with XRTpy
  • API reference for xrtpy.xrt_dem_iterative
  • Changelog entry in docs/changelog/0.6.0.rst

Validation -

  • Validated against IDL xrt_dem_iterative2.pro across multiple
    solar observation cases using Monte Carlo comparison
  • Currently - heavy work in progress (UPDATED JULY 2026)

@joyvelasquez joyvelasquez self-assigned this Jul 16, 2025
@joyvelasquez joyvelasquez added this to the 0.6.0 milestone Jul 16, 2025
@joyvelasquez

Copy link
Copy Markdown
Contributor Author

Ensure the function checks for minimum required inputs (filters, observed intensities, etc.) and validates that all observation values are non-zero before proceeding.

@codecov

codecov Bot commented Mar 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 2560 lines in your changes missing coverage. Please review.
✅ Project coverage is 36.28%. Comparing base (28a2e02) to head (bb258ef).

Files with missing lines Patch % Lines
xrtpy/xrt_dem_iterative/test/test_dem_solver.py 0.00% 502 Missing ⚠️
xrtpy/xrt_dem_iterative/dem_solver.py 0.00% 386 Missing ⚠️
...rative/test/older_testing_methods/plots_results.py 0.00% 294 Missing ⚠️
...ive/test/older_testing_methods/idl_vs_xrtpy_dem.py 0.00% 265 Missing ⚠️
xrtpy/xrt_dem_iterative/test/plot_mc_comparison.py 0.00% 211 Missing ⚠️
...t_dem_iterative/test/test_dem_idl_comparison_mc.py 0.00% 168 Missing ⚠️
xrtpy/xrt_dem_iterative/test/utils_sav_io.py 0.00% 164 Missing ⚠️
...py/xrt_dem_iterative/test/plot_idl_vs_xrtpy_dem.py 0.00% 149 Missing ⚠️
...rative/test/older_testing_methods/utils_case_io.py 0.00% 117 Missing ⚠️
.../xrt_dem_iterative/test/test_dem_idl_comparison.py 0.00% 114 Missing ⚠️
... and 4 more

❗ There is a different number of reports uploaded between BASE (28a2e02) and HEAD (bb258ef). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (28a2e02) HEAD (bb258ef)
2 1
Additional details and impacted files
@@             Coverage Diff             @@
##             main     #367       +/-   ##
===========================================
- Coverage   87.35%   36.28%   -51.07%     
===========================================
  Files          26       40       +14     
  Lines        1819     4379     +2560     
  Branches      106      334      +228     
===========================================
  Hits         1589     1589               
- Misses        184     2744     +2560     
  Partials       46       46               

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread docs/gallery/data_processing/deconvolving.py
Comment thread docs/gallery/data_processing/remove_lightleak.py
Comment thread docs/gallery/data_processing/temperature_from_filter_ratios.py
Comment thread docs/gallery/instrument_response/effective_area.py
Comment thread docs/about_xrt.rst
Comment thread docs/reference/index.rst
Comment on lines +12 to +14
util
xrt_dem_iterative
image_correction

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
util
xrt_dem_iterative
image_correction
xrt_dem_iterative
image_correction
util


.. automodule:: xrtpy.response.tools
:members:
:undoc-members:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎗️ I want to look up :undoc-members:

Comment on lines +1 to +4
*************************
xrtpy.xrt_dem_iterative
*************************

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*************************
xrtpy.xrt_dem_iterative
*************************
***********************
xrtpy.xrt_dem_iterative
***********************

@@ -0,0 +1,6 @@
*************************

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎗️ Does pre-commit have sphinx autoformatting whatever it was? 🤔

Comment thread docs/conf.py
from packaging.version import Version

# Ensure Sphinx imports the local xrtpy checkout (repo root), not site-packages
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎗️ Was there a new recommended Python way of doing this? 🤔

Comment thread docs/dem_overview.rst

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this would be better in a subdirectory maybe? 📂

Comment thread docs/dem_overview.rst

Hinode/XRT is well suited for DEM analysis because it observes the corona
through multiple broadband filters, each sensitive to different temperature
ranges. By combining these channels, we can infer a DEM(T) that reproduces

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ranges. By combining these channels, we can infer a DEM(T) that reproduces
ranges. By combining these channels, we can infer a :math:`\mathrm{DEM}(T)` that reproduces

Comment thread docs/dem_overview.rst
Comment on lines +3 to +6
===================================
Differential Emission Measure (DEM)
===================================

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
===================================
Differential Emission Measure (DEM)
===================================
==============================
Differential Emission Measures
==============================

Comment thread docs/dem_overview.rst
Introduction
------------

This page describes the XRTpy iterative DEM solver and the inputs and outputs needed to run it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This page describes the XRTpy iterative DEM solver and the inputs and outputs needed to run it.
This page describes the XRTpy iterative differential emission measure (DEM) solver and the inputs and outputs needed to run it.

Comment thread docs/dem_overview.rst

This page describes the XRTpy iterative DEM solver and the inputs and outputs needed to run it.

The differential emission measure (DEM) describes how much plasma is present

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The differential emission measure (DEM) describes how much plasma is present
The DEM describes how much plasma is present

Comment thread docs/dem_overview.rst
This page describes the XRTpy iterative DEM solver and the inputs and outputs needed to run it.

The differential emission measure (DEM) describes how much plasma is present
in the solar corona as a function of temperature. It is a key diagnostic for

@namurphy namurphy Jul 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
in the solar corona as a function of temperature. It is a key diagnostic for
in the solar corona as a function of temperature along a line of sight. DEMs are key diagnostics for

Comment thread docs/dem_overview.rst
1. Observed channels (filters)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Type: ``list`` of ``str``
* Description: Names of the filters used in the observation, for example ``"Al-mesh"`` or ``"Be-thin"``.

@namurphy namurphy Jul 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either list all of the filters here or link to where the filters are defined? (Maybe a glossary entry?) Linking would reduce duplication of text. 🤔

Comment thread docs/dem_overview.rst
Comment on lines +66 to +67


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Comment thread docs/dem_overview.rst
Comment on lines +77 to +81
responses = generate_temperature_responses(
filters,
"2012-07-10T12:03:20",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎗️ We should consider whether responses should be included as part of the interface defined in sunkit-dem.

Comment thread docs/dem_overview.rst

.. rubric:: Notes

This mirrors the "regular logT grid" used by the IDL routine ``xrt_dem_iterative2.pro``.

@namurphy namurphy Jul 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This mirrors the "regular logT grid" used by the IDL routine ``xrt_dem_iterative2.pro``.
This mirrors the "regular logT grid" option used by the IDL routine :file:`xrt_dem_iterative2.pro`.

Or "keyword"? I can't remember IDL. 😅

Comment thread docs/dem_overview.rst
The regular :math:`\log_{10}(T)` grid (dimensionless).

T : `~astropy.units.Quantity`
The linear temperature grid in Kelvin.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The linear temperature grid in Kelvin.
The linear temperature grid in kelvin.

Comment thread docs/dem_overview.rst
Comment on lines +119 to +122




Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Comment thread docs/dem_overview.rst
Comment on lines +186 to +187
Comparison with IDL
-------------------

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering about how to qualitatively (and perhaps also quantitatively?) describe the differences that users should expect.

Like...for temperatures where there isn't much plasma, I think there were higher relative errors, if I remember correctly.

Comment thread docs/dem_overview.rst
I_i = \int DEM(T)\, R_i(T)\, dT

where :math:`R_i(T)` is the temperature response function of the filter and
:math:`DEM(T)` describes the amount of emitting plasma as a function of

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:math:`DEM(T)` describes the amount of emitting plasma as a function of
:math:`\mathrm{DEM}(T)` describes the amount of emitting plasma as a function of

Comment thread docs/dem_overview.rst
SolarSoft/IDL routine `xrt_dem_iterative2.pro <https://hesperia.gsfc.nasa.gov/ssw/hinode/xrt/idl/util/xrt_dem_iterative2.pro>`__:

* Uses a regular :math:`\log_{10}(T)` grid.
* Represents :math:`\log_{10}(DEM)` at a set of spline knots.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Represents :math:`\log_{10}(DEM)` at a set of spline knots.
* Represents :math:`\log_{10}(\mathrm{DEM})` at a set of spline knots.

Comment thread docs/dem_overview.rst
The values shown above correspond to the solver defaults and are written
out here to illustrate which parameters can be tuned. You can adjust these
to suit your specific analysis needs. This mirrors the flexibility of the
IDL routine ``xrt_dem_iterative2.pro``.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
IDL routine ``xrt_dem_iterative2.pro``.
IDL routine :file:`xrt_dem_iterative2.pro`.

monte_carlo_runs=0,
)

x.validate_inputs()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should validate_inputs() be changed to _validate_inputs()? Is it something that needs to be user-facing?

Comment on lines +72 to +74
minimum_bound_temperature=5.5,
maximum_bound_temperature=7.5,
logarithmic_temperature_step_size=0.1,

@namurphy namurphy Jul 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
minimum_bound_temperature=5.5,
maximum_bound_temperature=7.5,
logarithmic_temperature_step_size=0.1,
**temperature_kwargs,

And define temperature_kwargs = {"minimum_bound_temperature": 5.5, ...} outside of the function, so that we can use keyword argument unpacking here?


x.create_logT_grid()

assert (x.logT[0], x.logT[-1], x.dlogT) == pytest.approx((5.5, 7.5, 0.1))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert (x.logT[0], x.logT[-1], x.dlogT) == pytest.approx((5.5, 7.5, 0.1))
np.testing.assert_allclose(
[x.logT[0], x.logT[-1], x.dlogT],
[5.5, 7.5, 0.1],
)

dem.validate_inputs()


def test_create_logT_grid():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be a good use case for pytest fixtures to avoid having repeated assertions, but it's probably not worth the effort changing here.

x.create_logT_grid()
x._interpolate_responses_to_grid()

dem, modeled, chi2, result = x._solve_single_dem(

@namurphy namurphy Jul 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎗️ I'm wondering if this would also be useful to think about adding upstream to the interface in sunkit-dem, especially if many DEM solvers are iterative. 🤔

temperature_responses=responses,
)
x.solve()
x.plot_dem()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎗️ Is there a way to do something like a regression test for figures?

Comment on lines +630 to +651
def test_init_rejects_negative_max_iterations():
"""max_iterations=-100 must raise ValueError.
Expected: ValueError because max_iterations must be a positive integer.
"""
filters = ["Al-poly", "Ti-poly"]
intensities = np.array([500.0, 800.0])
responses = generate_temperature_responses(filters, "2012-10-27T00:00:00")

with pytest.raises(ValueError, match="max_iterations must be a positive integer"):
XRTDEMIterative(filters, intensities, responses, max_iterations=-100)


def test_init_rejects_zero_normalization_factor():
"""normalization_factor=0 must raise ValueError.
Expected: ValueError because normalization_factor must be positive.
"""
filters = ["Al-poly", "Ti-poly"]
intensities = np.array([500.0, 800.0])
responses = generate_temperature_responses(filters, "2012-10-27T00:00:00")

with pytest.raises(ValueError, match="normalization_factor must be a positive"):
XRTDEMIterative(filters, intensities, responses, normalization_factor=0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might be able to use @pytest.mark.parametrize for some of these. 🤔

Comment on lines +794 to +795
filters = ["Al-poly", "Ti-poly", "Be-thin"]
intensities = np.array([100.0, 200.0, 300.0], dtype=float)

@namurphy namurphy Jul 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these be defined outside of the test functions to reduce duplication.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Code Improvement For general improvements to the codebase without adding new features. enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants