1use std::error::Error;
4use std::fmt::{Display, Formatter};
5
6use crate::{
7 CancellationToken, DiagnosticValue, LatticeBounds, ParameterChange, ParameterSet,
8 ParameterSpec, PreparedRietveldObjective, RefinementEventKind, RefinementLimits,
9 RefinementRuntime, ResidualOptions, RietveldCalculation, RietveldCalculationOptions,
10 RietveldError, RietveldInput, RietveldObjectiveError, RietveldParameterError, RietveldPhase,
11 RietveldStructuralLayout, RietveldStructuralSelection, RietveldTopologyChange, RuntimeError,
12 TerminationReason, calculate_rietveld_pattern, evaluate_residuals,
13};
14
15#[derive(Clone, Debug, PartialEq)]
17pub struct RietveldRefinementOptions {
18 pub calculation: RietveldCalculationOptions,
20 pub limits: RefinementLimits,
22 pub min_iterations: usize,
24 pub objective_tolerance: f64,
26 pub parameter_tolerance: f64,
28 pub initial_damping: f64,
30 pub damping_increase: f64,
32 pub damping_decrease: f64,
34 pub cg_tolerance: f64,
36 pub max_cg_iterations: usize,
38 pub max_scaled_parameter_step: f64,
40 pub max_backtracks: usize,
42}
43
44impl RietveldRefinementOptions {
45 #[allow(clippy::too_many_arguments)]
52 pub fn new(
53 calculation: RietveldCalculationOptions,
54 limits: RefinementLimits,
55 min_iterations: usize,
56 objective_tolerance: f64,
57 parameter_tolerance: f64,
58 initial_damping: f64,
59 damping_increase: f64,
60 damping_decrease: f64,
61 cg_tolerance: f64,
62 max_cg_iterations: usize,
63 max_scaled_parameter_step: f64,
64 max_backtracks: usize,
65 ) -> Result<Self, RietveldRefinementError> {
66 let result = Self {
67 calculation,
68 limits,
69 min_iterations,
70 objective_tolerance,
71 parameter_tolerance,
72 initial_damping,
73 damping_increase,
74 damping_decrease,
75 cg_tolerance,
76 max_cg_iterations,
77 max_scaled_parameter_step,
78 max_backtracks,
79 };
80 result.validate()?;
81 Ok(result)
82 }
83
84 pub fn validate(&self) -> Result<(), RietveldRefinementError> {
91 let positive = [
92 self.objective_tolerance,
93 self.parameter_tolerance,
94 self.initial_damping,
95 self.damping_increase,
96 self.damping_decrease,
97 self.cg_tolerance,
98 self.max_scaled_parameter_step,
99 ];
100 if self.min_iterations == 0
101 || self.min_iterations > self.limits.max_iterations()
102 || self.max_cg_iterations == 0
103 || positive
104 .iter()
105 .any(|value| !value.is_finite() || *value <= 0.0)
106 || self.damping_increase <= 1.0
107 || self.damping_decrease >= 1.0
108 {
109 return Err(RietveldRefinementError::InvalidOptions);
110 }
111 Ok(())
112 }
113}
114
115#[derive(Clone, Debug, PartialEq)]
117pub struct RietveldIterationRecord {
118 pub iteration: usize,
120 pub objective: f64,
122 pub objective_change: f64,
124 pub scaled_step_norm: f64,
126 pub damping: f64,
128 pub cg_iterations: usize,
130 pub backtracks: usize,
132 pub parameter_changes: Vec<ParameterChange>,
134 pub topology_changes: Vec<RietveldTopologyChange>,
136 pub rwp: f64,
138 pub rp: f64,
140 pub chi_square: f64,
142 pub reduced_chi_square: f64,
144}
145
146#[derive(Clone, Debug, PartialEq)]
148pub struct RietveldCheckpoint {
149 pub completed_iterations: usize,
151 pub phases: Vec<RietveldPhase>,
153 pub parameters: ParameterSet,
155 pub objective: f64,
157 pub damping: f64,
159 pub history: Vec<RietveldIterationRecord>,
161}
162
163impl RietveldCheckpoint {
164 fn validate(&self, input: &RietveldInput) -> Result<(), RietveldRefinementError> {
165 let phase_ids = input
166 .phases
167 .iter()
168 .map(RietveldPhase::phase_id)
169 .collect::<std::collections::BTreeSet<_>>();
170 if self.completed_iterations != self.history.len()
171 || self.phases.len() != input.phases.len()
172 || self
173 .phases
174 .iter()
175 .zip(&input.phases)
176 .any(|(stored, requested)| !stored.restart_compatible(requested))
177 || !self.objective.is_finite()
178 || self.objective < 0.0
179 || !self.damping.is_finite()
180 || self.damping <= 0.0
181 || self.history.iter().enumerate().any(|(index, row)| {
182 row.iteration != index + 1
183 || !row.objective.is_finite()
184 || row.objective < 0.0
185 || !row.objective_change.is_finite()
186 || row.objective_change < 0.0
187 || !row.scaled_step_norm.is_finite()
188 || row.scaled_step_norm < 0.0
189 || !row.damping.is_finite()
190 || row.damping <= 0.0
191 || !row.rwp.is_finite()
192 || !row.rp.is_finite()
193 || !row.chi_square.is_finite()
194 || row.chi_square < 0.0
195 || row.reduced_chi_square.is_nan()
196 || row.reduced_chi_square < 0.0
197 || row.parameter_changes.iter().any(|change| {
198 !change.before.is_finite()
199 || !change.after.is_finite()
200 || !change.scaled_change.is_finite()
201 })
202 || row.topology_changes.iter().any(|change| {
203 let added = change
204 .added_reflection_ids
205 .iter()
206 .collect::<std::collections::BTreeSet<_>>();
207 let removed = change
208 .removed_reflection_ids
209 .iter()
210 .collect::<std::collections::BTreeSet<_>>();
211 !phase_ids.contains(&change.phase_id)
212 || added.len() != change.added_reflection_ids.len()
213 || removed.len() != change.removed_reflection_ids.len()
214 || !added.is_disjoint(&removed)
215 })
216 })
217 {
218 return Err(RietveldRefinementError::InvalidCheckpoint);
219 }
220 Ok(())
221 }
222}
223
224#[derive(Clone, Debug, PartialEq)]
226pub struct RietveldRefinementResult {
227 pub calculation: RietveldCalculation,
229 pub phases: Vec<RietveldPhase>,
231 pub parameters: ParameterSet,
233 pub history: Vec<RietveldIterationRecord>,
235 pub termination_reason: TerminationReason,
237 pub checkpoint: RietveldCheckpoint,
239 pub evaluations: usize,
241}
242
243pub fn refine_rietveld(
250 input: &RietveldInput,
251 selection: RietveldStructuralSelection,
252 lattice_bounds: &[Option<LatticeBounds>],
253 options: &RietveldRefinementOptions,
254 checkpoint: Option<&RietveldCheckpoint>,
255 cancellation: Option<CancellationToken>,
256) -> Result<RietveldRefinementResult, RietveldRefinementError> {
257 let mut runtime = RefinementRuntime::new(options.limits, cancellation)?;
258 refine_rietveld_with_runtime(
259 input,
260 selection,
261 lattice_bounds,
262 options,
263 checkpoint,
264 &mut runtime,
265 )
266}
267
268#[allow(clippy::too_many_lines)]
275pub fn refine_rietveld_with_runtime(
276 input: &RietveldInput,
277 selection: RietveldStructuralSelection,
278 lattice_bounds: &[Option<LatticeBounds>],
279 options: &RietveldRefinementOptions,
280 checkpoint: Option<&RietveldCheckpoint>,
281 runtime: &mut RefinementRuntime<RietveldCheckpoint>,
282) -> Result<RietveldRefinementResult, RietveldRefinementError> {
283 input.validate()?;
284 options.validate()?;
285 let (mut phases, mut history, mut damping) = if let Some(checkpoint) = checkpoint {
286 checkpoint.validate(input)?;
287 let expected =
288 RietveldStructuralLayout::new(&checkpoint.phases, selection, lattice_bounds)?;
289 if expected.parameters() != &checkpoint.parameters {
290 return Err(RietveldRefinementError::InvalidCheckpoint);
291 }
292 runtime.resume_accepted(checkpoint.completed_iterations)?;
293 (
294 checkpoint.phases.clone(),
295 checkpoint.history.clone(),
296 checkpoint.damping,
297 )
298 } else {
299 (input.phases.clone(), Vec::new(), options.initial_damping)
300 };
301 runtime.emit(
302 RefinementEventKind::Start,
303 "rietveld",
304 "native structural refinement started",
305 Vec::new(),
306 )?;
307 let has_observations = input
308 .pattern
309 .mask
310 .as_ref()
311 .is_none_or(|mask| mask.iter().any(|included| *included));
312 let mut termination = if has_observations {
313 TerminationReason::MaxIterations
314 } else {
315 TerminationReason::NoObservations
316 };
317 let first_iteration = history.len() + 1;
318 let last_iteration = if has_observations {
319 options.limits.max_iterations()
320 } else {
321 history.len()
322 };
323 'iterations: for iteration in first_iteration..=last_iteration {
324 if let Err(error) = runtime.begin_iteration(iteration) {
325 termination = normal_stop(&error)?;
326 break;
327 }
328 let layout = RietveldStructuralLayout::new(&phases, selection, lattice_bounds)?;
329 let specs = layout.parameters().specs();
330 if specs.is_empty() {
331 termination = TerminationReason::Converged;
332 break;
333 }
334 let live_input = replace_phases(input, phases.clone())?;
335 let objective = PreparedRietveldObjective::new(
336 live_input.clone(),
337 options.calculation.clone(),
338 layout.clone(),
339 )?;
340 if let Err(error) = reserve_products(runtime, 2) {
341 termination = normal_stop(&error)?;
342 break;
343 }
344 let (_, physical_gradient) = objective.gradient()?;
345 let scales = specs.iter().map(ParameterSpec::scale).collect::<Vec<_>>();
346 let scaled_gradient = physical_gradient
347 .iter()
348 .zip(&scales)
349 .map(|(gradient, scale)| gradient * scale)
350 .collect::<Vec<_>>();
351 let right_hand_side = scaled_gradient
352 .iter()
353 .map(|value| -value)
354 .collect::<Vec<_>>();
355 let solve = conjugate_gradient(
356 &right_hand_side,
357 options.cg_tolerance,
358 options.max_cg_iterations,
359 |direction| {
360 reserve_products(runtime, 2)?;
361 let physical = direction
362 .iter()
363 .zip(&scales)
364 .map(|(value, scale)| value * scale)
365 .collect::<Vec<_>>();
366 let product = objective.normal_product(&physical, 0.0)?;
367 Ok(product
368 .iter()
369 .zip(&scales)
370 .zip(direction)
371 .map(|((value, scale), direction)| value * scale + damping * direction)
372 .collect())
373 },
374 );
375 let (mut step, cg_iterations) = match solve {
376 Ok(result) => result,
377 Err(RietveldRefinementError::Runtime(RuntimeError::Stopped(stop))) => {
378 termination = stop.reason;
379 break 'iterations;
380 }
381 Err(error) => return Err(error),
382 };
383 let mut step_norm = norm(&step);
384 if step_norm > options.max_scaled_parameter_step {
385 let factor = options.max_scaled_parameter_step / step_norm;
386 for value in &mut step {
387 *value *= factor;
388 }
389 step_norm = options.max_scaled_parameter_step;
390 }
391 if step_norm < options.parameter_tolerance {
392 termination = TerminationReason::Converged;
393 break;
394 }
395 if let Err(error) = runtime.begin_evaluation() {
396 termination = normal_stop(&error)?;
397 break;
398 }
399 let current_calculation = calculate_rietveld_pattern(&live_input, &options.calculation)?;
400 let current_objective = 0.5 * current_calculation.metrics.chi_square;
401 let current_values = specs.iter().map(ParameterSpec::value).collect::<Vec<_>>();
402 let mut accepted = None;
403 for backtrack in 0..=options.max_backtracks {
404 let factor = 0.5_f64.powi(i32::try_from(backtrack).unwrap_or(i32::MAX));
405 let trial_values = specs
406 .iter()
407 .zip(¤t_values)
408 .zip(step.iter().zip(&scales))
409 .map(|((spec, current), (step, scale))| {
410 spec.bounds().clip(current + factor * step * scale)
411 })
412 .collect::<Vec<_>>();
413 let trial_phases = layout.apply_values(&phases, &trial_values)?;
414 if let Err(error) = runtime.begin_evaluation() {
415 termination = normal_stop(&error)?;
416 break 'iterations;
417 }
418 let trial_input = replace_phases(input, trial_phases.clone())?;
419 let trial_calculation = calculate_rietveld_pattern(&trial_input, &options.calculation)?;
420 let trial_objective = 0.5 * trial_calculation.metrics.chi_square;
421 runtime.emit(
422 RefinementEventKind::Trial,
423 "rietveld_step",
424 "native structural trial evaluated",
425 vec![(
426 "objective".to_owned(),
427 DiagnosticValue::Float(trial_objective),
428 )],
429 )?;
430 if trial_objective < current_objective {
431 accepted = Some((
432 backtrack,
433 factor,
434 trial_values,
435 trial_phases,
436 trial_calculation,
437 trial_objective,
438 ));
439 break;
440 }
441 if let Err(error) = runtime.reject_step() {
442 termination = normal_stop(&error)?;
443 break;
444 }
445 }
446 let Some((backtracks, factor, trial_values, trial_phases, trial_calculation, objective)) =
447 accepted
448 else {
449 damping *= options.damping_increase;
450 if termination == TerminationReason::MaxIterations {
451 continue;
452 }
453 break;
454 };
455 let objective_change = current_objective - objective;
456 let parameter_changes = specs
457 .iter()
458 .zip(¤t_values)
459 .zip(&trial_values)
460 .filter(|((_, before), after)| before.to_bits() != after.to_bits())
461 .map(|((spec, before), after)| ParameterChange {
462 key: spec.key().clone(),
463 before: *before,
464 after: *after,
465 scaled_change: (after - before) / spec.scale(),
466 })
467 .collect::<Vec<_>>();
468 let topology_changes = phases
469 .iter()
470 .zip(&trial_phases)
471 .filter_map(|(before, after)| topology_change(before, after))
472 .collect::<Vec<_>>();
473 let accepted_metrics = evaluate_residuals(
474 &input.pattern,
475 &trial_calculation.y,
476 ResidualOptions {
477 use_uncertainty: options.calculation.use_uncertainty,
478 parameter_count: specs.len(),
479 },
480 )?;
481 history.push(RietveldIterationRecord {
482 iteration: history.len() + 1,
483 objective,
484 objective_change,
485 scaled_step_norm: factor * step_norm,
486 damping,
487 cg_iterations,
488 backtracks,
489 parameter_changes,
490 topology_changes: topology_changes.clone(),
491 rwp: accepted_metrics.rwp,
492 rp: accepted_metrics.rp,
493 chi_square: accepted_metrics.chi_square,
494 reduced_chi_square: accepted_metrics.reduced_chi_square,
495 });
496 phases = trial_phases;
497 damping = (damping * options.damping_decrease).max(1.0e-18);
498 let accepted_layout = RietveldStructuralLayout::new(&phases, selection, lattice_bounds)?;
499 let state = RietveldCheckpoint {
500 completed_iterations: history.len(),
501 phases: phases.clone(),
502 parameters: accepted_layout.parameters().clone(),
503 objective,
504 damping,
505 history: history.clone(),
506 };
507 runtime.accept_step(Some(&state))?;
508 runtime.emit(
509 RefinementEventKind::StepAccepted,
510 "rietveld_step",
511 "native structural step accepted",
512 vec![
513 ("objective".to_owned(), DiagnosticValue::Float(objective)),
514 (
515 "topology_changes".to_owned(),
516 DiagnosticValue::Integer(
517 i64::try_from(topology_changes.len()).unwrap_or(i64::MAX),
518 ),
519 ),
520 ],
521 )?;
522 if history.len() >= options.min_iterations
523 && objective_change <= options.objective_tolerance * objective.max(1.0)
524 {
525 termination = TerminationReason::Converged;
526 break;
527 }
528 }
529 let final_layout = RietveldStructuralLayout::new(&phases, selection, lattice_bounds)?;
530 let final_input = replace_phases(input, phases.clone())?;
531 runtime.begin_evaluation().or_else(|error| match error {
532 RuntimeError::Stopped(_) => Ok(()),
533 other => Err(other),
534 })?;
535 let mut calculation = calculate_rietveld_pattern(&final_input, &options.calculation)?;
536 calculation.metrics = evaluate_residuals(
537 &input.pattern,
538 &calculation.y,
539 ResidualOptions {
540 use_uncertainty: options.calculation.use_uncertainty,
541 parameter_count: final_layout.parameters().specs().len(),
542 },
543 )?;
544 let checkpoint = RietveldCheckpoint {
545 completed_iterations: history.len(),
546 phases: phases.clone(),
547 parameters: final_layout.parameters().clone(),
548 objective: 0.5 * calculation.metrics.chi_square,
549 damping,
550 history: history.clone(),
551 };
552 checkpoint.validate(input)?;
553 runtime.emit(
554 RefinementEventKind::Termination,
555 "rietveld",
556 "native structural refinement terminated",
557 vec![(
558 "reason".to_owned(),
559 DiagnosticValue::String(termination.as_str().to_owned()),
560 )],
561 )?;
562 Ok(RietveldRefinementResult {
563 calculation,
564 phases,
565 parameters: final_layout.parameters().clone(),
566 history,
567 termination_reason: termination,
568 checkpoint,
569 evaluations: runtime.evaluations(),
570 })
571}
572
573pub(crate) fn topology_change(
574 before: &RietveldPhase,
575 after: &RietveldPhase,
576) -> Option<RietveldTopologyChange> {
577 let previous = before
578 .reflection_ids()
579 .iter()
580 .collect::<std::collections::BTreeSet<_>>();
581 let current = after
582 .reflection_ids()
583 .iter()
584 .collect::<std::collections::BTreeSet<_>>();
585 let added_reflection_ids = after
586 .reflection_ids()
587 .iter()
588 .filter(|id| !previous.contains(id))
589 .cloned()
590 .collect::<Vec<_>>();
591 let removed_reflection_ids = before
592 .reflection_ids()
593 .iter()
594 .filter(|id| !current.contains(id))
595 .cloned()
596 .collect::<Vec<_>>();
597 if added_reflection_ids.is_empty() && removed_reflection_ids.is_empty() {
598 return None;
599 }
600 Some(RietveldTopologyChange {
601 phase_id: after.phase_id().clone(),
602 added_reflection_ids,
603 removed_reflection_ids,
604 preserved_reflection_count: current.intersection(&previous).count(),
605 })
606}
607
608fn replace_phases(
609 input: &RietveldInput,
610 phases: Vec<RietveldPhase>,
611) -> Result<RietveldInput, RietveldError> {
612 let mut replaced = input.clone();
613 replaced.phases = phases;
614 replaced.validate()?;
615 Ok(replaced)
616}
617
618pub(crate) fn reserve_products<T>(
619 runtime: &mut RefinementRuntime<T>,
620 count: usize,
621) -> Result<(), RuntimeError> {
622 for _ in 0..count {
623 runtime.begin_evaluation()?;
624 }
625 Ok(())
626}
627
628pub(crate) fn conjugate_gradient(
629 right_hand_side: &[f64],
630 tolerance: f64,
631 max_iterations: usize,
632 operator: impl FnMut(&[f64]) -> Result<Vec<f64>, RietveldRefinementError>,
633) -> Result<(Vec<f64>, usize), RietveldRefinementError> {
634 conjugate_gradient_core(right_hand_side, tolerance, max_iterations, operator).map_err(|error| {
635 match error {
636 ConjugateGradientError::Operator(error) => error,
637 ConjugateGradientError::NonPositiveOperator => {
638 RietveldRefinementError::NonPositiveNormalOperator
639 }
640 ConjugateGradientError::NonFiniteState => RietveldRefinementError::NonFiniteSolve,
641 }
642 })
643}
644
645pub(crate) enum ConjugateGradientError<E> {
646 Operator(E),
647 NonPositiveOperator,
648 NonFiniteState,
649}
650
651pub(crate) fn conjugate_gradient_core<E>(
652 right_hand_side: &[f64],
653 tolerance: f64,
654 max_iterations: usize,
655 mut operator: impl FnMut(&[f64]) -> Result<Vec<f64>, E>,
656) -> Result<(Vec<f64>, usize), ConjugateGradientError<E>> {
657 let mut solution = vec![0.0; right_hand_side.len()];
658 let mut residual = right_hand_side.to_vec();
659 let mut direction = residual.clone();
660 let mut squared = dot(&residual, &residual);
661 let target = tolerance * norm(right_hand_side).max(1.0);
662 if squared.sqrt() <= target {
663 return Ok((solution, 0));
664 }
665 for iteration in 1..=max_iterations {
666 let product = operator(&direction).map_err(ConjugateGradientError::Operator)?;
667 let denominator = dot(&direction, &product);
668 if !denominator.is_finite() || denominator <= 0.0 {
669 return Err(ConjugateGradientError::NonPositiveOperator);
670 }
671 let alpha = squared / denominator;
672 for index in 0..solution.len() {
673 solution[index] += alpha * direction[index];
674 residual[index] -= alpha * product[index];
675 }
676 let next_squared = dot(&residual, &residual);
677 if !next_squared.is_finite() {
678 return Err(ConjugateGradientError::NonFiniteState);
679 }
680 if next_squared.sqrt() <= target {
681 return Ok((solution, iteration));
682 }
683 let beta = next_squared / squared;
684 for index in 0..direction.len() {
685 direction[index] = residual[index] + beta * direction[index];
686 }
687 squared = next_squared;
688 }
689 Ok((solution, max_iterations))
690}
691
692fn dot(left: &[f64], right: &[f64]) -> f64 {
693 left.iter()
694 .zip(right)
695 .map(|(left, right)| left * right)
696 .sum()
697}
698
699pub(crate) fn norm(values: &[f64]) -> f64 {
700 dot(values, values).sqrt()
701}
702
703pub(crate) fn normal_stop(
704 error: &RuntimeError,
705) -> Result<TerminationReason, RietveldRefinementError> {
706 if let RuntimeError::Stopped(stop) = error {
707 Ok(stop.reason)
708 } else {
709 Err(RietveldRefinementError::RuntimeMessage(error.to_string()))
710 }
711}
712
713#[derive(Debug)]
715pub enum RietveldRefinementError {
716 InvalidOptions,
718 InvalidCheckpoint,
720 NonPositiveNormalOperator,
722 NonFiniteSolve,
724 RuntimeMessage(String),
726 Rietveld(RietveldError),
728 Parameter(RietveldParameterError),
730 GeneralParameter(crate::RietveldGeneralParameterError),
732 Objective(RietveldObjectiveError),
734 GeneralObjective(crate::RietveldGeneralObjectiveError),
736 Constraint(crate::ConstraintError),
738 Runtime(RuntimeError),
740 Residual(crate::ResidualError),
742}
743
744impl Display for RietveldRefinementError {
745 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
746 match self {
747 Self::InvalidOptions => formatter.write_str("native Rietveld options are invalid"),
748 Self::InvalidCheckpoint => formatter.write_str("native Rietveld checkpoint is invalid"),
749 Self::NonPositiveNormalOperator => {
750 formatter.write_str("native Rietveld normal operator is not positive definite")
751 }
752 Self::NonFiniteSolve => formatter.write_str("native Rietveld solve became non-finite"),
753 Self::RuntimeMessage(message) => formatter.write_str(message),
754 Self::Rietveld(error) => Display::fmt(error, formatter),
755 Self::Parameter(error) => Display::fmt(error, formatter),
756 Self::GeneralParameter(error) => Display::fmt(error, formatter),
757 Self::Objective(error) => Display::fmt(error, formatter),
758 Self::GeneralObjective(error) => Display::fmt(error, formatter),
759 Self::Constraint(error) => Display::fmt(error, formatter),
760 Self::Runtime(error) => Display::fmt(error, formatter),
761 Self::Residual(error) => Display::fmt(error, formatter),
762 }
763 }
764}
765
766impl Error for RietveldRefinementError {
767 fn source(&self) -> Option<&(dyn Error + 'static)> {
768 match self {
769 Self::Rietveld(error) => Some(error),
770 Self::Parameter(error) => Some(error),
771 Self::GeneralParameter(error) => Some(error),
772 Self::Objective(error) => Some(error),
773 Self::GeneralObjective(error) => Some(error),
774 Self::Constraint(error) => Some(error),
775 Self::Runtime(error) => Some(error),
776 Self::Residual(error) => Some(error),
777 Self::InvalidOptions
778 | Self::InvalidCheckpoint
779 | Self::NonPositiveNormalOperator
780 | Self::NonFiniteSolve
781 | Self::RuntimeMessage(_) => None,
782 }
783 }
784}
785impl From<RietveldError> for RietveldRefinementError {
786 fn from(value: RietveldError) -> Self {
787 Self::Rietveld(value)
788 }
789}
790impl From<RietveldParameterError> for RietveldRefinementError {
791 fn from(value: RietveldParameterError) -> Self {
792 Self::Parameter(value)
793 }
794}
795impl From<RietveldObjectiveError> for RietveldRefinementError {
796 fn from(value: RietveldObjectiveError) -> Self {
797 Self::Objective(value)
798 }
799}
800impl From<crate::RietveldGeneralParameterError> for RietveldRefinementError {
801 fn from(value: crate::RietveldGeneralParameterError) -> Self {
802 Self::GeneralParameter(value)
803 }
804}
805impl From<crate::RietveldGeneralObjectiveError> for RietveldRefinementError {
806 fn from(value: crate::RietveldGeneralObjectiveError) -> Self {
807 Self::GeneralObjective(value)
808 }
809}
810impl From<crate::ConstraintError> for RietveldRefinementError {
811 fn from(value: crate::ConstraintError) -> Self {
812 Self::Constraint(value)
813 }
814}
815impl From<RuntimeError> for RietveldRefinementError {
816 fn from(value: RuntimeError) -> Self {
817 Self::Runtime(value)
818 }
819}
820impl From<crate::ResidualError> for RietveldRefinementError {
821 fn from(value: crate::ResidualError) -> Self {
822 Self::Residual(value)
823 }
824}