|
13 | 13 | from scipy import integrate, optimize, stats |
14 | 14 |
|
15 | 15 | from sasdata.dataloader.data_info import Data1D |
16 | | -from sasmodels.core import load_model |
17 | | -from sasmodels.direct_model import DirectModel |
| 16 | +from sasmodels.core import build_model, load_model_info |
| 17 | +from sasmodels.direct_model import call_Fq |
18 | 18 |
|
19 | 19 | from sas.sascalc.size_distribution.maxEnt_method import maxEntMethod |
20 | 20 |
|
@@ -114,6 +114,7 @@ def background_fit( |
114 | 114 | # Fit only scale |
115 | 115 | def fit_func(x: npt.ArrayLike, b: float) -> npt.ArrayLike: |
116 | 116 | return line_func(x, b, power) |
| 117 | + |
117 | 118 | init_guess = linearized_data.y[0] |
118 | 119 |
|
119 | 120 | else: |
@@ -177,6 +178,7 @@ def __init__(self, data: Data1D): |
177 | 178 | self._resolution: float | None = None |
178 | 179 |
|
179 | 180 | self.model_matrix: np.ndarray | None = None |
| 181 | + self._volumes = None |
180 | 182 |
|
181 | 183 | # Advanced parameters for MaxEnt |
182 | 184 | self._iterMax: int = 5000 |
@@ -483,24 +485,45 @@ def generate_model_matrix(self, moddata: Data1D) -> None: |
483 | 485 | :param moddata: Data1D object that has the data trimmed depending on background |
484 | 486 | subtraction or powerlaw subtracted from the data. Also self.qMin and self.qMax. |
485 | 487 | """ |
486 | | - model = load_model(self.model) |
487 | | - |
488 | 488 | pars = { |
489 | 489 | "sld": self.contrast, |
490 | 490 | "sld_solvent": 0.0, |
491 | 491 | "background": 0.0, |
492 | 492 | "scale": 1.0, |
493 | 493 | } |
494 | 494 |
|
495 | | - kernel = DirectModel(moddata, model) |
496 | | - |
497 | 495 | intensities = [] |
| 496 | + volumes = [] |
| 497 | + |
| 498 | + # Build a single Kernel to compute both intensity and volume per bin |
| 499 | + model_info = load_model_info(self.model) |
| 500 | + model_obj = build_model(model_info) |
| 501 | + calculator = model_obj.make_kernel((moddata.x,)) |
| 502 | + |
498 | 503 | for bin in self.bins: |
499 | | - pars["radius_equatorial"] = bin |
500 | | - pars["radius_polar"] = bin * self.aspectRatio |
501 | | - intensities.append(kernel(**pars)) |
| 504 | + p = pars.copy() |
| 505 | + p["radius_equatorial"] = bin |
| 506 | + p["radius_polar"] = bin * self.aspectRatio |
| 507 | + |
| 508 | + call_pars = p.copy() |
| 509 | + _, Fsq, _, volume, volume_ratio = call_Fq(calculator, call_pars) |
| 510 | + |
| 511 | + # Compute intensity using kernel convention: combined_scale = scale / shell_volume |
| 512 | + scale_val = p.get("scale", 1.0) |
| 513 | + background_val = p.get("background", 0.0) |
| 514 | + combined_scale = scale_val / volume |
| 515 | + |
| 516 | + intensity = combined_scale * Fsq + background_val |
| 517 | + intensities.append(intensity) |
| 518 | + |
| 519 | + # Volume ratio is 1 for solid particles |
| 520 | + if volume_ratio is not None: |
| 521 | + volume *= volume_ratio |
| 522 | + |
| 523 | + volumes.append(volume) |
502 | 524 |
|
503 | 525 | self.model_matrix = np.vstack(intensities).T |
| 526 | + self._volumes = np.array(volumes) |
504 | 527 |
|
505 | 528 | def calc_volume_weighted_dist(self, binmag: np.ndarray) -> None: |
506 | 529 | """ |
@@ -669,7 +692,7 @@ def calculate_statistics(self, bin_mag: npt.ArrayLike) -> None: |
669 | 692 | """ |
670 | 693 | Calculate statistics from the MaxEnt results, including volume fraction cumulative distribution function (CDF), |
671 | 694 | number distribution, and related statistics such as mean, median, and mode. |
672 | | - |
| 695 | +
|
673 | 696 | :param bin_mag: list of bin magnitudes from the MaxEnt fits |
674 | 697 | """ |
675 | 698 | bin_mag = np.asarray(bin_mag) |
|
0 commit comments