2424
2525
2626class Analysis :
27+ """High-level orchestration of analysis tasks for a Project.
28+
29+ This class wires calculators and minimizers, exposes a compact
30+ interface for parameters, constraints and results, and coordinates
31+ computations across the project's sample models and experiments.
32+
33+ Typical usage:
34+
35+ - Display or filter parameters to fit.
36+ - Select a calculator/minimizer implementation.
37+ - Calculate patterns and run single or joint fits.
38+
39+ Attributes:
40+ project: The parent Project object.
41+ aliases: A registry of human-friendly aliases for parameters.
42+ constraints: Symbolic constraints between parameters.
43+ calculator: Active calculator used for computations.
44+ fitter: Active fitter/minimizer driver.
45+ """
46+
2747 _calculator = CalculatorFactory .create_calculator ('cryspy' )
2848
2949 def __init__ (self , project ) -> None :
50+ """Create a new Analysis instance bound to a project.
51+
52+ Args:
53+ project: The project that owns models and experiments.
54+ """
3055 self .project = project
3156 self .aliases = Aliases ()
3257 self .constraints = Constraints ()
@@ -79,6 +104,9 @@ def _get_params_as_dataframe(
79104 return dataframe
80105
81106 def show_all_params (self ) -> None :
107+ """Print a table with all parameters for sample models and
108+ experiments.
109+ """
82110 sample_models_params = self .project .sample_models .parameters
83111 experiments_params = self .project .experiments .parameters
84112
@@ -126,6 +154,9 @@ def show_all_params(self) -> None:
126154 )
127155
128156 def show_fittable_params (self ) -> None :
157+ """Print a table with parameters that can be included in
158+ fitting.
159+ """
129160 sample_models_params = self .project .sample_models .fittable_parameters
130161 experiments_params = self .project .experiments .fittable_parameters
131162
@@ -177,6 +208,9 @@ def show_fittable_params(self) -> None:
177208 )
178209
179210 def show_free_params (self ) -> None :
211+ """Print a table with only currently-free (varying)
212+ parameters.
213+ """
180214 sample_models_params = self .project .sample_models .free_parameters
181215 experiments_params = self .project .experiments .free_parameters
182216 free_params = sample_models_params + experiments_params
@@ -225,6 +259,13 @@ def show_free_params(self) -> None:
225259 )
226260
227261 def how_to_access_parameters (self ) -> None :
262+ """Show Python access paths and CIF unique IDs for all
263+ parameters.
264+
265+ The output explains how to reference specific parameters in code
266+ and which unique identifiers are used when creating CIF-based
267+ constraints.
268+ """
228269 sample_models_params = self .project .sample_models .parameters
229270 experiments_params = self .project .experiments .parameters
230271 all_params = {
@@ -289,19 +330,31 @@ def how_to_access_parameters(self) -> None:
289330 )
290331
291332 def show_current_calculator (self ) -> None :
333+ """Print the name of the currently selected calculator
334+ engine.
335+ """
292336 print (paragraph ('Current calculator' ))
293337 print (self .current_calculator )
294338
295339 @staticmethod
296340 def show_supported_calculators () -> None :
341+ """Print a table of available calculator backends on this
342+ system.
343+ """
297344 CalculatorFactory .show_supported_calculators ()
298345
299346 @property
300347 def current_calculator (self ) -> str :
348+ """The key/name of the active calculator backend."""
301349 return self ._calculator_key
302350
303351 @current_calculator .setter
304352 def current_calculator (self , calculator_name : str ) -> None :
353+ """Switch to a different calculator backend.
354+
355+ Args:
356+ calculator_name: Calculator key to use (e.g. 'cryspy').
357+ """
305358 calculator = CalculatorFactory .create_calculator (calculator_name )
306359 if calculator is None :
307360 return
@@ -311,29 +364,53 @@ def current_calculator(self, calculator_name: str) -> None:
311364 print (self .current_calculator )
312365
313366 def show_current_minimizer (self ) -> None :
367+ """Print the name of the currently selected minimizer."""
314368 print (paragraph ('Current minimizer' ))
315369 print (self .current_minimizer )
316370
317371 @staticmethod
318372 def show_available_minimizers () -> None :
373+ """Print a table of available minimizer drivers on this
374+ system.
375+ """
319376 MinimizerFactory .show_available_minimizers ()
320377
321378 @property
322379 def current_minimizer (self ) -> Optional [str ]:
380+ """The identifier of the active minimizer, if any."""
323381 return self .fitter .selection if self .fitter else None
324382
325383 @current_minimizer .setter
326384 def current_minimizer (self , selection : str ) -> None :
385+ """Switch to a different minimizer implementation.
386+
387+ Args:
388+ selection: Minimizer selection string, e.g.
389+ 'lmfit (leastsq)'.
390+ """
327391 self .fitter = Fitter (selection )
328392 print (paragraph ('Current minimizer changed to' ))
329393 print (self .current_minimizer )
330394
331395 @property
332396 def fit_mode (self ) -> str :
397+ """Current fitting strategy: either 'single' or 'joint'."""
333398 return self ._fit_mode
334399
335400 @fit_mode .setter
336401 def fit_mode (self , strategy : str ) -> None :
402+ """Set the fitting strategy.
403+
404+ When set to 'joint', all experiments get default weights and
405+ are used together in a single optimization.
406+
407+ Args:
408+ strategy: Either 'single' or 'joint'.
409+
410+ Raises:
411+ ValueError: If an unsupported strategy value is
412+ provided.
413+ """
337414 if strategy not in ['single' , 'joint' ]:
338415 raise ValueError ("Fit mode must be either 'single' or 'joint'" )
339416 self ._fit_mode = strategy
@@ -346,6 +423,9 @@ def fit_mode(self, strategy: str) -> None:
346423 print (self ._fit_mode )
347424
348425 def show_available_fit_modes (self ) -> None :
426+ """Print all supported fitting strategies and their
427+ descriptions.
428+ """
349429 strategies = [
350430 {
351431 'Strategy' : 'single' ,
@@ -374,21 +454,25 @@ def show_available_fit_modes(self) -> None:
374454 )
375455
376456 def show_current_fit_mode (self ) -> None :
457+ """Print the currently active fitting strategy."""
377458 print (paragraph ('Current fit mode' ))
378459 print (self .fit_mode )
379460
380461 def calculate_pattern (self , expt_name : str ) -> None :
381- """Calculate the diffraction pattern for a given experiment. The
382- calculated pattern is stored within the experiment's datastore.
462+ """Calculate and store the diffraction pattern for an
463+ experiment.
464+
465+ The pattern is stored in the target experiment's datastore.
383466
384467 Args:
385- expt_name: The name of the experiment.
468+ expt_name: The identifier of the experiment to compute .
386469 """
387470 experiment = self .project .experiments [expt_name ]
388471 sample_models = self .project .sample_models
389472 self .calculator .calculate_pattern (sample_models , experiment )
390473
391474 def show_constraints (self ) -> None :
475+ """Print a table of all user-defined symbolic constraints."""
392476 constraints_dict = dict (self .constraints )
393477
394478 if not self .constraints ._items :
@@ -416,6 +500,9 @@ def show_constraints(self) -> None:
416500 )
417501
418502 def apply_constraints (self ):
503+ """Apply the currently defined constraints to the active
504+ project.
505+ """
419506 if not self .constraints ._items :
420507 print (warning ('No constraints defined.' ))
421508 return
@@ -425,6 +512,14 @@ def apply_constraints(self):
425512 self .constraints_handler .apply ()
426513
427514 def fit (self ):
515+ """Execute fitting using the selected mode, calculator and
516+ minimizer.
517+
518+ In 'single' mode, fits each experiment independently. In
519+ 'joint' mode, performs a simultaneous fit across experiments
520+ with weights.
521+ Sets :attr:`fit_results` on success.
522+ """
428523 sample_models = self .project .sample_models
429524 if not sample_models :
430525 print ('No sample models found in the project. Cannot run fit.' )
@@ -471,11 +566,19 @@ def fit(self):
471566 self .fit_results = self .fitter .results
472567
473568 def as_cif (self ):
569+ """Serialize the analysis section to a CIF string.
570+
571+ Returns:
572+ The analysis section represented as a CIF document string.
573+ """
474574 from easydiffraction .io .cif .serialize import analysis_to_cif
475575
476576 return analysis_to_cif (self )
477577
478578 def show_as_cif (self ) -> None :
579+ """Render the analysis section as CIF in a formatted console
580+ view.
581+ """
479582 cif_text : str = self .as_cif ()
480583 paragraph_title : str = paragraph ('Analysis 🧮 info as cif' )
481584 render_cif (cif_text , paragraph_title )
0 commit comments