11# -*- coding: utf-8 -*-
22
33from copy import deepcopy
4- from typing import Optional
4+ from typing import Any
55
66import numpy as np
77from lmfit import Parameters , minimize
@@ -179,14 +179,14 @@ def fit_polynom_through_origin(x, y , degree: int) -> np.ndarray:
179179
180180
181181from .calc import calculate_pdf
182- from .configuration import CalculationConfig , DataConfig
182+ from .configuration import CalculationConfig , DataConfig , Result
183183
184184
185185def optimize_density (
186186 data_config : DataConfig ,
187187 calculation_config : CalculationConfig ,
188188 method : str = "fr" ,
189- min_range : Optional [ tuple [float , float ]] = None ,
189+ min_range : tuple [float , float ] | None = None ,
190190 vary_bkg_scaling : bool = True ,
191191 bkg_limits : tuple [float , float ] = (0.9 , 1.1 ),
192192 optimization_method : str = "lsq" ,
@@ -272,17 +272,30 @@ def optimize_density(
272272 )
273273
274274 optim_config = calculation_config .model_copy (deep = True )
275+ reference_result : Result | None = None
276+ range_limits : tuple [float , float ] | None = min_range
275277
276278 if method == "sq" :
277279 reference_config = calculation_config .model_copy (deep = True )
278280 reference_config .optimize = None
279281 reference_result = calculate_pdf (data_config , reference_config )
280- if min_range is None :
281- min_range = (0 , reference_config .transform .q_max )
282+ if range_limits is None :
283+ range_limits = (0 , reference_config .transform .q_max )
284+ elif method in ("gr" , "fr" ):
285+ if range_limits is None :
286+ optimize_settings = optim_config .optimize
287+ if optimize_settings is None :
288+ raise ValueError (
289+ "Optimization range cannot be inferred because calculation_config.optimize is None."
290+ )
291+ range_limits = (0 , optimize_settings .r_cutoff )
292+ else :
293+ raise ValueError (
294+ f"Invalid optimize density method: { method } , only 'gr', 'fr' and 'sq' are supported."
295+ )
282296
283- elif method == "gr" or method == "fr" :
284- if min_range is None :
285- min_range = (0 , optim_config .optimize .r_cutoff )
297+ if range_limits is None :
298+ raise ValueError ("Optimization range must be specified." )
286299
287300 def fcn (params ):
288301 density = params ["density" ].value
@@ -292,46 +305,52 @@ def fcn(params):
292305 result = calculate_pdf (data_config , optim_config )
293306
294307 if method == "gr" :
295- r , gr = result .gr .limit (* min_range ).data
308+ if result .gr is None :
309+ raise ValueError ("Result does not contain g(r) data required for 'gr' optimization." )
310+ r , gr = result .gr .limit (* range_limits ).data
296311 residual = gr * (r [1 ] - r [0 ])
297312 elif method == "fr" :
313+ if result .fr is None :
314+ raise ValueError ("Result does not contain F(r) data required for 'fr' optimization." )
298315 atomic_density = optim_config .sample .atomic_density
299- r , fr = result .fr .limit (* min_range ).data
316+ if atomic_density is None :
317+ raise ValueError ("Sample atomic density must be set for 'fr' optimization." )
318+ r , fr = result .fr .limit (* range_limits ).data
300319 residual = (fr + 4 * np .pi * r * atomic_density ) * (r [1 ] - r [0 ])
301320 elif method == "sq" :
302- q , sq = result .sq .limit (* min_range ).data
303- sq_ref = reference_result .sq .limit (* min_range ).y
321+ if reference_result is None or reference_result .sq is None :
322+ raise ValueError ("Reference result does not contain S(q) data required for 'sq' optimization." )
323+ if result .sq is None :
324+ raise ValueError ("Result does not contain S(q) data required for 'sq' optimization." )
325+ q , sq = result .sq .limit (* range_limits ).data
326+ sq_ref = reference_result .sq .limit (* range_limits ).y
304327 residual = (sq - sq_ref ) * (q [1 ] - q [0 ])
305- else :
306- raise ValueError (
307- f"Invalid optimize density method: { method } , only 'gr', 'fr' and 'sq' are supported."
308- )
309328 return residual
310329
311330 if optimization_method == "nelder" :
312- res = minimize (
331+ nelder_res : Any = minimize (
313332 fcn ,
314333 params ,
315334 method = "nelder" ,
316335 options = {"maxfev" : 500 , "fatol" : 0.0001 , "xatol" : 0.0001 },
317336 )
318337 return (
319- res .params ["density" ].value ,
320- np .sum (res .residual ** 2 ),
321- res .params ["bkg_scaling" ].value ,
322- np .sum (res .residual ** 2 ),
338+ nelder_res .params ["density" ].value ,
339+ np .sum (nelder_res .residual ** 2 ),
340+ nelder_res .params ["bkg_scaling" ].value ,
341+ np .sum (nelder_res .residual ** 2 ),
323342 )
324343 elif optimization_method == "lsq" :
325- res = minimize (
344+ lsq_res : Any = minimize (
326345 fcn ,
327346 params ,
328347 method = "least_squares" ,
329348 )
330349 return (
331- res .params ["density" ].value ,
332- res .params ["density" ].stderr ,
333- res .params ["bkg_scaling" ].value ,
334- res .params ["bkg_scaling" ].stderr ,
350+ lsq_res .params ["density" ].value ,
351+ lsq_res .params ["density" ].stderr ,
352+ lsq_res .params ["bkg_scaling" ].value ,
353+ lsq_res .params ["bkg_scaling" ].stderr ,
335354 )
336355 else :
337356 raise ValueError (f"Invalid optimization method: { optimization_method } " )
0 commit comments