11from __future__ import annotations
22
3- import warnings
4- from typing import Union
3+ from typing import Optional , Union
54
65import numpy as np
76import scipp as sc
87from easyscience .variable import Parameter
98
9+ from easydynamics .sample_model .components .mixins import CreateParametersMixin
10+
1011from .model_component import ModelComponent
1112
1213Numeric = Union [float , int ]
1314
14- MINIMUM_WIDTH = 1e-10 # To avoid division by zero
15-
1615
17- class DampedHarmonicOscillator (ModelComponent ):
16+ class DampedHarmonicOscillator (CreateParametersMixin , ModelComponent ):
1817 """
1918 Damped Harmonic Oscillator (DHO). 2*area*center^2*width/pi / ( (x^2 - center^2)^2 + (2*width*x)^2 )
2019
@@ -28,97 +27,30 @@ class DampedHarmonicOscillator(ModelComponent):
2827
2928 def __init__ (
3029 self ,
31- name : str = "DHO " ,
32- center : Numeric = 1.0 ,
33- width : Numeric = 1.0 ,
34- area : Numeric = 1.0 ,
35- unit : Union [str , sc .Unit ] = "meV" ,
30+ name : Optional [ str ] = "DampedHarmonicOscillator " ,
31+ area : Optional [ Union [ Numeric , Parameter ]] = 1.0 ,
32+ center : Optional [ Union [ Numeric , Parameter ]] = 1.0 ,
33+ width : Optional [ Union [ Numeric , Parameter ]] = 1.0 ,
34+ unit : Optional [ Union [str , sc .Unit ] ] = "meV" ,
3635 ):
37- # Validate inputs
38- if not isinstance (area , Numeric ):
39- raise TypeError ("area must be a number." )
40- area = float (area )
41- if area < 0 :
42- warnings .warn (
43- "The area of the Damped Harmonic Oscillator with name {} is negative, which may not be physically meaningful." .format (
44- name
45- )
46- )
47-
48- if not isinstance (center , Numeric ):
49- raise TypeError ("center must be a number." )
50-
51- center = float (center )
52-
53- if not isinstance (width , Numeric ):
54- raise TypeError ("width must be a number." )
55-
56- width = float (width )
57- if width <= 0 :
58- raise ValueError (
59- "The width of a DampedHarmonicOscillator must be greater than zero."
60- )
61-
62- super ().__init__ (name = name , unit = unit )
63-
64- # Create Parameters from floats
65- self ._area = Parameter (name = name + " area" , value = area , unit = unit )
66- if area > 0 :
67- self ._area .min = 0.0
68-
69- self ._center = Parameter (name = name + " center" , value = center , unit = unit )
70-
71- self ._width = Parameter (
72- name = name + " width" , value = width , unit = unit , min = MINIMUM_WIDTH
36+ # Validate inputs and create Parameters if not given
37+ self .validate_unit (unit )
38+ self ._unit = unit
39+
40+ # These methods live in ValidationMixin
41+ area = self ._create_area_parameter (area = area , name = name , unit = self ._unit )
42+ center = self ._create_center_parameter (
43+ center = center , name = name , fix_if_none = False , unit = self ._unit
7344 )
45+ width = self ._create_width_parameter (width = width , name = name , unit = self ._unit )
7446
75- @property
76- def area (self ) -> Parameter :
77- """Return the area parameter."""
78- return self ._area
79-
80- @area .setter
81- def area (self , value : Numeric ):
82- """Set the area parameter."""
83- if not isinstance (value , Numeric ):
84- raise TypeError ("area must be a number." )
85- value = float (value )
86- if value < 0 :
87- warnings .warn (
88- "The area of the Damped Harmonic Oscillator with name {} is negative, which may not be physically meaningful." .format (
89- self .name
90- )
91- )
92- self ._area .value = float (value )
93-
94- @property
95- def center (self ) -> Parameter :
96- """Return the center parameter."""
97- return self ._center
98-
99- @center .setter
100- def center (self , value : Numeric ):
101- """Set the center parameter."""
102- if not isinstance (value , Numeric ):
103- raise TypeError ("center must be a number." )
104- self ._center .value = float (value )
105-
106- @property
107- def width (self ) -> Parameter :
108- """Return the width parameter."""
109- return self ._width
110-
111- @width .setter
112- def width (self , value : Numeric ):
113- """Set the width parameter."""
114- if not isinstance (value , Numeric ):
115- raise TypeError ("width must be a number." )
116- value = float (value )
117- if value <= 0 :
118- raise ValueError (
119- "The width of a DampedHarmonicOscillator must be greater than zero."
120- )
121- self ._width .value = value
47+ super ().__init__ (
48+ name = name ,
49+ unit = unit ,
50+ area = area ,
51+ center = center ,
52+ width = width ,
53+ )
12254
12355 def evaluate (
12456 self , x : Union [Numeric , list , np .ndarray , sc .Variable , sc .DataArray ]
@@ -129,53 +61,14 @@ def evaluate(
12961
13062 x = self ._prepare_x_for_evaluate (x )
13163
132- normalization = 2 * self ._center .value ** 2 * self ._width .value / np .pi
133- denominator = (x ** 2 - self ._center .value ** 2 ) ** 2 + (
64+ normalization = 2 * self .center .value ** 2 * self .width .value / np .pi
65+ denominator = (x ** 2 - self .center .value ** 2 ) ** 2 + (
13466 2
135- * self ._width .value
67+ * self .width .value
13668 * x # No division by zero here, width>0 enforced in setter
13769 ) ** 2
13870
139- return self ._area .value * normalization / (denominator )
140-
141- def get_parameters (self ):
142- """
143- Get all parameters from the model component.
144- Returns:
145- List[Parameter]: List of parameters in the component.
146- """
147- return [self ._area , self ._center , self ._width ]
148-
149- def convert_unit (self , unit : Union [str , sc .Unit ]):
150- """
151- Convert the unit of the Parameters in the component.
152-
153- Args:
154- unit (str or sc.Unit): The new unit to convert to.
155- """
156-
157- self ._area .convert_unit (unit )
158- self ._center .convert_unit (unit )
159- self ._width .convert_unit (unit )
160- self ._unit = unit
161-
162- def __copy__ (self ) -> DampedHarmonicOscillator :
163- """
164- Return a deep copy of this component with independent parameters.
165- """
166- name = "copy of " + self .name
167-
168- model_copy = DampedHarmonicOscillator (
169- name = name ,
170- area = self ._area .value ,
171- center = self ._center .value ,
172- width = self ._width .value ,
173- unit = self ._unit ,
174- )
175- model_copy ._area .fixed = self ._area .fixed
176- model_copy ._center .fixed = self ._center .fixed
177- model_copy ._width .fixed = self ._width .fixed
178- return model_copy
71+ return self .area .value * normalization / (denominator )
17972
18073 def __repr__ (self ):
181- return f"DampedHarmonicOscillator(name = { self .name } , unit = { self ._unit } ,\n area = { self ._area } ,\n center = { self ._center } ,\n width = { self ._width } )"
74+ return f"DampedHarmonicOscillator(name = { self .name } , unit = { self ._unit } ,\n area = { self .area } ,\n center = { self .center } ,\n width = { self .width } )"
0 commit comments