fpex0.setup

  1from copy import copy
  2
  3import numpy as np
  4from scipy.integrate import solve_ivp
  5from scipy import interpolate
  6
  7from .fokker_planck import FokkerPlanck
  8
  9
 10class DSC_Data:
 11    """
 12    Class for dsc data. Stores unprocessed and processed data.
 13    
 14    ## Parameters
 15    **mass**
 16    <br> Sample mass.
 17
 18    **rate**
 19    <br> Heating rate.
 20    
 21    **T**
 22    <br> Temperatures.
 23    
 24    **dsc**
 25    <br> Corresponding raw DSC measurements.
 26    
 27    **bldata** 
 28    <br> Information about the baselevel.
 29    
 30    **blfun** 
 31    <br> Baselevel function as retrieved by `fpex0.baseline.getBaseline`.
 32    
 33    **cp** 
 34    <br> Calculated heat capacities as HeatCapacitiy member.
 35
 36    **ID**
 37    <br> Name/identifier of experiment.
 38    """
 39    def __init__(self, T=None, dsc=None, mass=None, rate=None, bldata=None, blfun=None, cp=None, ID=None):
 40        self.mass      = mass     
 41        self.rate      = rate     
 42        self.T         = T        
 43        self.dsc       = dsc      
 44        self.bldata    = bldata   
 45        self.blfun     = blfun
 46        self.cp        = cp
 47        self.ID        = ID
 48
 49
 50
 51class Setup:
 52    """
 53    FPEX0 configuration.
 54
 55    ## Parameters : `fpex0.setup.Grid`
 56    **Grid**
 57    <br> A discretization grid for Fokker-Planck.
 58    
 59    **Parameters**
 60    <br> A collection of parameters and boudns, 
 61    instance of `fpex0.setup.Parameters`.
 62    
 63    **Integration** : `fpex0.setup.Integration`
 64    <br> Represents an integrator to solve our discretization of Fokker-Planck.
 65
 66    **FPdriftFcn** 
 67    <br> Function object of Fokker-Planck drift.
 68    
 69    **FPdiffusionFcn**
 70    <br> Function object of Fokker-Planck diffusion.
 71    
 72    **IniDistFcn**
 73    <br> Function object of inital distribution.
 74    """
 75    def __init__(self, Grid, Parameters : dict, Integration, FPdriftFcn, FPdiffusionFcn, IniDistFcn):
 76        self.Grid           = Grid
 77        self.Parameters     = Parameters
 78        self.Integration    = Integration
 79        self.FPdriftFcn     = FPdriftFcn
 80        self.FPdiffusionFcn = FPdiffusionFcn
 81        self.IniDistFcn     = IniDistFcn
 82        
 83        self.Measurements   = Measurements()
 84
 85
 86    def make_rhsFcn(self, p_FPdrift, p_FPdiffusion):
 87        """
 88        Generator for the ODEs right-hand-side.
 89        """
 90        h = self.Grid.h
 91        FPdriftFcn = self.FPdriftFcn
 92        FPdiffusionFcn = self.FPdiffusionFcn
 93
 94        rhsFcn = lambda t,u: FokkerPlanck.FokkerPlanckODE(t, u, self.Grid.h, self.FPdriftFcn, p_FPdrift, self.FPdiffusionFcn, p_FPdiffusion)
 95        return rhsFcn
 96
 97
 98    def make_jacFcn(self, p_FPdrift, p_FPdiffusion):
 99        """
100        Generator for the ODEs jacobian.
101        """
102        fokker_planck = FokkerPlanck()
103        jacFcn = lambda t,u: fokker_planck.FokkerPlanckODE_dfdu(t, u, self.Grid.h, self.FPdriftFcn, p_FPdrift, self.FPdiffusionFcn, p_FPdiffusion)
104        return jacFcn
105
106
107    def importMeasurements(self, T, values, heatrate, ID=None, gridskip=1):
108        """
109        Imports measurements into FPEX0setup (`fpex0.setup.Measurements`).
110        An interpolation to the integration grid is performed and the interpolated values are stored.
111        
112        ## Takes 
113        **FPEX0setup**
114        <br> FPEX0_class_setup object.
115        
116        **ID** 
117        <br> Name/identifier of experiment.
118        
119        **heatrate**
120        <br> Heating rate.
121        
122        **T**
123        <br> Temperature grid / measurement positions.
124        
125        **values**
126        <br> Measurement values (e.g. cp-values); 
127        where values[k] denotes the measurement value at T[k].
128        
129        **gridskip**
130        <br> Use grid with given stepsize (gridskip). 
131        <br> For gridskip=2 for example, only every second grid point is used,
132        whereas gridskip=1 uses the whole grid.
133        
134
135        ## Returns
136        No return value.
137        """
138        
139        # ensure that the rate (=time) lies inside the gridTdot
140        if heatrate < self.Grid.gridTdot[0] or heatrate > self.Grid.gridTdot[-1]:
141           print(f'importMeasurements: Specified rate {heatrate} outside grid bounds [{self.Grid.gridTdot[0]}, {self.Grid.gridTdot[-1]}] !')
142        
143        # ensure we have a single column
144        rawT    = np.reshape(T     , -1)  # passing -1 creates a 1d array
145        values  = np.reshape(values, -1)
146
147        # interpolate values to gridT respecting gridskip
148        rawValues = values
149        temperatures = self.Grid.gridT[::gridskip]
150        interp = interpolate.PchipInterpolator(rawT, rawValues, extrapolate=False)
151        values = interp(temperatures)
152        values = np.where(np.isnan(values), 0, values)   # replace NaN by 0
153        
154        # add measurements
155        self.Measurements.appendMeasurement(rawT, temperatures, rawValues, values, heatrate, ID)
156
157
158    def importDSCobject(self, DSC_Data, gridskip=1):
159        # T, values, heatrate, ID=None, gridskip=1
160
161        if type(DSC_Data) is list:
162            for i in range(len(DSC_Data)):
163                self.importDSCobject(DSC_Data[i], gridskip)
164            return
165
166        T = DSC_Data.T
167        # use cp data if available, raw dsc otherwise
168        if DSC_Data.cp is not None:
169            values = DSC_Data.cp
170        else: 
171            values = DSC_Data.dsc
172            print("WARNING: Using unprocessed dsc data. Make sure it is baseline-corrected!")
173        heatrate = DSC_Data.rate
174        ID = DSC_Data.ID
175
176        self.importMeasurements(T, values, heatrate, ID, gridskip)
177
178
179class Parameters:
180    """
181    ## Parameters
182
183    **p0**
184    <br> Full initial parameter vector.
185
186    **p_lb**
187    <br> Full parameter lower bound vector.
188    
189    **p_ub**
190    <br> Full parameter upper bound vector.
191
192    **p0_FPdrift**
193    <br> Initial drift parameter vector.
194
195    **p0_FPdiffusion**
196    <br> Initial diffusion parameter vector.
197    
198    **p0_iniDist**
199    <br> Initial paramater vector for initial distribution.
200    
201    **p_lb_FPdrift**
202    <br> Lower bound vector for drift parameters.
203
204    **p_lb_FPdiffusion**
205    <br> ...
206    
207    **p_lb_iniDist**
208    <br> ...
209    
210    **p_ub_FPdrift**
211    <br> Upper bound vector for drift parameters.
212    
213    **p_ub_FPdiffusion**
214    <br> ...
215    
216    **p_ub_iniDist**
217    <br> ...
218
219    **idxFPdrift**
220    <br> Indices of drift parameters in p0 (resp. p_lb, p_ub).
221
222    **idxFPdiffusion**
223    <br> Same thing, diffusion parameters. 
224    
225    **idxFPall**
226    <br> Same thing, all Fokker-Planck parameters.
227    
228    **idxIniDist**
229    <br> Same thing, initial distribution parameters.
230    """
231    def __init__(self,  p0_FPdrift,        p0_FPdiffusion,        p0_iniDist,
232                        p_lb_FPdrift=None, p_lb_FPdiffusion=None, p_lb_iniDist=None,
233                        p_ub_FPdrift=None, p_ub_FPdiffusion=None, p_ub_iniDist=None ):
234
235        # ensure matching lengths
236        assert(p_lb_FPdrift is None or len(p0_FPdrift) == len(p_lb_FPdrift) 
237           and p_ub_FPdrift is None or len(p0_FPdrift) == len(p_ub_FPdrift))
238
239        assert(p_lb_FPdiffusion is None or len(p0_FPdiffusion) == len(p_lb_FPdiffusion) 
240           and p_ub_FPdiffusion is None or len(p0_FPdiffusion) == len(p_ub_FPdiffusion))
241
242        assert(p_lb_iniDist is None or len(p0_iniDist) == len(p_lb_iniDist) 
243           and p_ub_iniDist is None or len(p0_iniDist) == len(p_ub_iniDist))
244
245        # initial parameters
246        self.p0_FPdrift         = p0_FPdrift
247        self.p0_FPdiffusion     = p0_FPdiffusion
248        self.p0_iniDist         = p0_iniDist
249
250        # lower parameter bounds
251        # set to vectors of -np.inf if not given
252        if p_lb_FPdrift is None:
253            self.p_lb_FPdrift = np.full(len(p0_FPdrift), -np.inf)
254        else:
255            self.p_lb_FPdrift = p_lb_FPdrift
256
257        if p_lb_FPdiffusion is None:
258            self.p_lb_FPdiffusion = np.full(len(p0_FPdiffusion), -np.inf)
259        else:
260            self.p_lb_FPdiffusion = p_lb_FPdiffusion
261
262        if p_lb_iniDist is None:
263            self.p_lb_iniDist = np.full(len(p0_iniDist), -np.inf)
264        else:
265            self.p_lb_iniDist = p_lb_iniDist
266
267        # upper parameter bounds
268        # set to vectors of np.inf if not given
269        if p_ub_FPdrift is None:
270            self.p_ub_FPdrift = np.full(len(p0_FPdrift), np.inf)
271        else:
272            self.p_ub_FPdrift = p_ub_FPdrift
273
274        if p_ub_FPdiffusion is None:
275            self.p_ub_FPdiffusion = np.full(len(p0_FPdiffusion), np.inf)
276        else:
277            self.p_ub_FPdiffusion   = p_ub_FPdiffusion
278
279        if p_ub_iniDist is None:
280            self.p_ub_iniDist = np.full(len(p0_iniDist), np.inf)
281        else:
282            self.p_ub_iniDist       = p_ub_iniDist
283
284        # put initials, lower and upper bounds in one vector each
285        self.p0 = np.concatenate((p0_FPdrift, p0_FPdiffusion, p0_iniDist))
286        self.p_lb = np.concatenate((p_lb_FPdrift, p_lb_FPdiffusion, p_lb_iniDist))
287        self.p_ub = np.concatenate((p_ub_FPdrift, p_ub_FPdiffusion, p_ub_iniDist))
288        
289        # indices
290        self.idxFPdrift      = np.arange(0, len(p0_FPdrift) )
291        self.idxFPdiffusion  = np.arange(0, len(p0_FPdiffusion) ) + len(p0_FPdrift)
292        self.idxFPall        = np.arange(0, len(p0_FPdrift)+len(p0_FPdiffusion) )
293        self.idxIniDist      = np.arange(0, len(p0_iniDist) ) + ( len(p0_FPdrift) + len(p0_FPdiffusion) )
294
295
296    # extractor functions for external parameter vector (alike self.p0)
297    def extract_p_all(self, pvec):
298        return pvec
299
300    def extract_p_FPdrift(self, pvec):
301        return pvec[self.idxFPdrift]
302
303    def extract_p_FPdiffusion(self, pvec):
304        return pvec[self.idxFPdiffusion]
305
306    def extract_p_FPall(self, pvec):
307        return pvec[self.idxFPall]
308
309    def extract_p_iniDist(self, pvec):
310        return pvec[self.idxIniDist]
311
312
313    # setter functions (TODO: Set parameters in the vectors.)
314    def set_p0(self, p0):
315        self.p0 = p0
316
317    def set_p_lb(self, p_lb):
318        assert(len(p_lb) == len(self.p0))
319        self.p_lb = p_lb
320    
321    def set_p_ub(self, p_ub):
322        assert(len(p_ub) == len(self.p0))
323        self.p_ub = p_ub
324
325
326
327class Grid:
328    """
329    FPEX0 discretization(?) grid.
330
331    ## Parameters
332    **gridT**
333    <br> x-grid = temperatures
334    <br> This is our method of lines discretization grid.
335    
336    **gridTdot**
337    <br> t-grid = heating rates
338    
339    **N** 
340    <br> Number of grid points.
341    
342    **h** 
343    <br> Grid size.
344    """
345    def __init__(self, gridT, gridTdot):
346        self.gridT = gridT
347        self.gridTdot = gridTdot
348        self.N = len(gridT)
349        self.h = ( gridT[-1] - gridT[0] ) / self.N
350
351
352
353class Measurements:
354    """
355    Stores measurement data in list containers.
356    
357    ## Parameters
358     **rawT** 
359     <br> Measurement temperatures.
360     
361     **temperatures** 
362     <br> Selected temperatures from temperature grid.
363     
364     **rawValues** 
365     <br> Raw masurement values.
366     
367     **values** 
368     <br> rawValues interpolated to grid.
369     
370     **rates** 
371     <br> Heat rates.
372     
373     **ID** 
374     <br> Experiment name/identifier.
375    """
376    def __init__(self, rawT=[], temperatures=[], rawValues=[], values=[], rates=[], ID=[]):
377        self.rawT           = rawT
378        self.temperatures   = temperatures
379        self.rawValues      = rawValues
380        self.rates          = rates
381        self.ID             = ID
382        self.values         = values
383
384    def appendMeasurement(self, new_T, new_temperatures, new_rawValues, new_values, new_rate, new_ID=None):
385        """
386        Appends given measurements to the measurement parameters. 
387        """
388        self.rawT.append(new_T)
389        self.temperatures.append(new_temperatures)
390        self.rawValues.append(new_rawValues)
391        self.values.append(new_values)
392        self.rates.append(new_rate)
393        self.ID.append(new_ID)
394        
395
396
397class Integration:
398    """
399    Represents FPEX0 integrator.
400
401    ## Parameters
402    **method**
403    <br> Method to use by scipy solve_ivp. Default is BDF.
404    
405    **options**
406    <br> Integration options. Currently not used. 
407    
408    **integrator**
409    <br> Integrator for ODEs. Should have the signature integrator(FPrhs, t0tf, u0, method).
410
411    **NN, A, B**: Internal variables used by FokkerPlanckODE_dfdu.
412    """
413    # (TODO) Set default tolerances in Integration class (?)
414    def __init__(self, integrator=solve_ivp, method="BDF", options={}):
415        self.method     = method
416        self.options    = options
417        self.integrator = integrator
418        self.NN         = None  # variables for FokkerPlanckODE_dfdu()
419        self.A          = None
420        self.B          = None
421
422    def updateOptions(self, newOptions):
423        """ Returns the integrators options. """
424        options = copy(self.options)
425        for key in newOptions:
426            options[key] = newOptions[key]
427        self.options = options
428        return options
429    
430
431    def updateJacobian(self, jacobianFcn):
432        """ Returns an option dictionary with updated jacobian. """
433        options = copy(self.options)
434        options["jac"] = jacobianFcn
435        self.options = options
436        return options
437        
class DSC_Data:
11class DSC_Data:
12    """
13    Class for dsc data. Stores unprocessed and processed data.
14    
15    ## Parameters
16    **mass**
17    <br> Sample mass.
18
19    **rate**
20    <br> Heating rate.
21    
22    **T**
23    <br> Temperatures.
24    
25    **dsc**
26    <br> Corresponding raw DSC measurements.
27    
28    **bldata** 
29    <br> Information about the baselevel.
30    
31    **blfun** 
32    <br> Baselevel function as retrieved by `fpex0.baseline.getBaseline`.
33    
34    **cp** 
35    <br> Calculated heat capacities as HeatCapacitiy member.
36
37    **ID**
38    <br> Name/identifier of experiment.
39    """
40    def __init__(self, T=None, dsc=None, mass=None, rate=None, bldata=None, blfun=None, cp=None, ID=None):
41        self.mass      = mass     
42        self.rate      = rate     
43        self.T         = T        
44        self.dsc       = dsc      
45        self.bldata    = bldata   
46        self.blfun     = blfun
47        self.cp        = cp
48        self.ID        = ID

Class for dsc data. Stores unprocessed and processed data.

Parameters

mass
Sample mass.

rate
Heating rate.

T
Temperatures.

dsc
Corresponding raw DSC measurements.

bldata
Information about the baselevel.

blfun
Baselevel function as retrieved by fpex0.baseline.getBaseline.

cp
Calculated heat capacities as HeatCapacitiy member.

ID
Name/identifier of experiment.

DSC_Data( T=None, dsc=None, mass=None, rate=None, bldata=None, blfun=None, cp=None, ID=None)
40    def __init__(self, T=None, dsc=None, mass=None, rate=None, bldata=None, blfun=None, cp=None, ID=None):
41        self.mass      = mass     
42        self.rate      = rate     
43        self.T         = T        
44        self.dsc       = dsc      
45        self.bldata    = bldata   
46        self.blfun     = blfun
47        self.cp        = cp
48        self.ID        = ID
class Setup:
 52class Setup:
 53    """
 54    FPEX0 configuration.
 55
 56    ## Parameters : `fpex0.setup.Grid`
 57    **Grid**
 58    <br> A discretization grid for Fokker-Planck.
 59    
 60    **Parameters**
 61    <br> A collection of parameters and boudns, 
 62    instance of `fpex0.setup.Parameters`.
 63    
 64    **Integration** : `fpex0.setup.Integration`
 65    <br> Represents an integrator to solve our discretization of Fokker-Planck.
 66
 67    **FPdriftFcn** 
 68    <br> Function object of Fokker-Planck drift.
 69    
 70    **FPdiffusionFcn**
 71    <br> Function object of Fokker-Planck diffusion.
 72    
 73    **IniDistFcn**
 74    <br> Function object of inital distribution.
 75    """
 76    def __init__(self, Grid, Parameters : dict, Integration, FPdriftFcn, FPdiffusionFcn, IniDistFcn):
 77        self.Grid           = Grid
 78        self.Parameters     = Parameters
 79        self.Integration    = Integration
 80        self.FPdriftFcn     = FPdriftFcn
 81        self.FPdiffusionFcn = FPdiffusionFcn
 82        self.IniDistFcn     = IniDistFcn
 83        
 84        self.Measurements   = Measurements()
 85
 86
 87    def make_rhsFcn(self, p_FPdrift, p_FPdiffusion):
 88        """
 89        Generator for the ODEs right-hand-side.
 90        """
 91        h = self.Grid.h
 92        FPdriftFcn = self.FPdriftFcn
 93        FPdiffusionFcn = self.FPdiffusionFcn
 94
 95        rhsFcn = lambda t,u: FokkerPlanck.FokkerPlanckODE(t, u, self.Grid.h, self.FPdriftFcn, p_FPdrift, self.FPdiffusionFcn, p_FPdiffusion)
 96        return rhsFcn
 97
 98
 99    def make_jacFcn(self, p_FPdrift, p_FPdiffusion):
100        """
101        Generator for the ODEs jacobian.
102        """
103        fokker_planck = FokkerPlanck()
104        jacFcn = lambda t,u: fokker_planck.FokkerPlanckODE_dfdu(t, u, self.Grid.h, self.FPdriftFcn, p_FPdrift, self.FPdiffusionFcn, p_FPdiffusion)
105        return jacFcn
106
107
108    def importMeasurements(self, T, values, heatrate, ID=None, gridskip=1):
109        """
110        Imports measurements into FPEX0setup (`fpex0.setup.Measurements`).
111        An interpolation to the integration grid is performed and the interpolated values are stored.
112        
113        ## Takes 
114        **FPEX0setup**
115        <br> FPEX0_class_setup object.
116        
117        **ID** 
118        <br> Name/identifier of experiment.
119        
120        **heatrate**
121        <br> Heating rate.
122        
123        **T**
124        <br> Temperature grid / measurement positions.
125        
126        **values**
127        <br> Measurement values (e.g. cp-values); 
128        where values[k] denotes the measurement value at T[k].
129        
130        **gridskip**
131        <br> Use grid with given stepsize (gridskip). 
132        <br> For gridskip=2 for example, only every second grid point is used,
133        whereas gridskip=1 uses the whole grid.
134        
135
136        ## Returns
137        No return value.
138        """
139        
140        # ensure that the rate (=time) lies inside the gridTdot
141        if heatrate < self.Grid.gridTdot[0] or heatrate > self.Grid.gridTdot[-1]:
142           print(f'importMeasurements: Specified rate {heatrate} outside grid bounds [{self.Grid.gridTdot[0]}, {self.Grid.gridTdot[-1]}] !')
143        
144        # ensure we have a single column
145        rawT    = np.reshape(T     , -1)  # passing -1 creates a 1d array
146        values  = np.reshape(values, -1)
147
148        # interpolate values to gridT respecting gridskip
149        rawValues = values
150        temperatures = self.Grid.gridT[::gridskip]
151        interp = interpolate.PchipInterpolator(rawT, rawValues, extrapolate=False)
152        values = interp(temperatures)
153        values = np.where(np.isnan(values), 0, values)   # replace NaN by 0
154        
155        # add measurements
156        self.Measurements.appendMeasurement(rawT, temperatures, rawValues, values, heatrate, ID)
157
158
159    def importDSCobject(self, DSC_Data, gridskip=1):
160        # T, values, heatrate, ID=None, gridskip=1
161
162        if type(DSC_Data) is list:
163            for i in range(len(DSC_Data)):
164                self.importDSCobject(DSC_Data[i], gridskip)
165            return
166
167        T = DSC_Data.T
168        # use cp data if available, raw dsc otherwise
169        if DSC_Data.cp is not None:
170            values = DSC_Data.cp
171        else: 
172            values = DSC_Data.dsc
173            print("WARNING: Using unprocessed dsc data. Make sure it is baseline-corrected!")
174        heatrate = DSC_Data.rate
175        ID = DSC_Data.ID
176
177        self.importMeasurements(T, values, heatrate, ID, gridskip)

FPEX0 configuration.

Parameters : fpex0.setup.Grid

Grid
A discretization grid for Fokker-Planck.

Parameters
A collection of parameters and boudns, instance of fpex0.setup.Parameters.

Integration : fpex0.setup.Integration
Represents an integrator to solve our discretization of Fokker-Planck.

FPdriftFcn
Function object of Fokker-Planck drift.

FPdiffusionFcn
Function object of Fokker-Planck diffusion.

IniDistFcn
Function object of inital distribution.

Setup( Grid, Parameters: dict, Integration, FPdriftFcn, FPdiffusionFcn, IniDistFcn)
76    def __init__(self, Grid, Parameters : dict, Integration, FPdriftFcn, FPdiffusionFcn, IniDistFcn):
77        self.Grid           = Grid
78        self.Parameters     = Parameters
79        self.Integration    = Integration
80        self.FPdriftFcn     = FPdriftFcn
81        self.FPdiffusionFcn = FPdiffusionFcn
82        self.IniDistFcn     = IniDistFcn
83        
84        self.Measurements   = Measurements()
def make_rhsFcn(self, p_FPdrift, p_FPdiffusion):
87    def make_rhsFcn(self, p_FPdrift, p_FPdiffusion):
88        """
89        Generator for the ODEs right-hand-side.
90        """
91        h = self.Grid.h
92        FPdriftFcn = self.FPdriftFcn
93        FPdiffusionFcn = self.FPdiffusionFcn
94
95        rhsFcn = lambda t,u: FokkerPlanck.FokkerPlanckODE(t, u, self.Grid.h, self.FPdriftFcn, p_FPdrift, self.FPdiffusionFcn, p_FPdiffusion)
96        return rhsFcn

Generator for the ODEs right-hand-side.

def make_jacFcn(self, p_FPdrift, p_FPdiffusion):
 99    def make_jacFcn(self, p_FPdrift, p_FPdiffusion):
100        """
101        Generator for the ODEs jacobian.
102        """
103        fokker_planck = FokkerPlanck()
104        jacFcn = lambda t,u: fokker_planck.FokkerPlanckODE_dfdu(t, u, self.Grid.h, self.FPdriftFcn, p_FPdrift, self.FPdiffusionFcn, p_FPdiffusion)
105        return jacFcn

Generator for the ODEs jacobian.

def importMeasurements(self, T, values, heatrate, ID=None, gridskip=1):
108    def importMeasurements(self, T, values, heatrate, ID=None, gridskip=1):
109        """
110        Imports measurements into FPEX0setup (`fpex0.setup.Measurements`).
111        An interpolation to the integration grid is performed and the interpolated values are stored.
112        
113        ## Takes 
114        **FPEX0setup**
115        <br> FPEX0_class_setup object.
116        
117        **ID** 
118        <br> Name/identifier of experiment.
119        
120        **heatrate**
121        <br> Heating rate.
122        
123        **T**
124        <br> Temperature grid / measurement positions.
125        
126        **values**
127        <br> Measurement values (e.g. cp-values); 
128        where values[k] denotes the measurement value at T[k].
129        
130        **gridskip**
131        <br> Use grid with given stepsize (gridskip). 
132        <br> For gridskip=2 for example, only every second grid point is used,
133        whereas gridskip=1 uses the whole grid.
134        
135
136        ## Returns
137        No return value.
138        """
139        
140        # ensure that the rate (=time) lies inside the gridTdot
141        if heatrate < self.Grid.gridTdot[0] or heatrate > self.Grid.gridTdot[-1]:
142           print(f'importMeasurements: Specified rate {heatrate} outside grid bounds [{self.Grid.gridTdot[0]}, {self.Grid.gridTdot[-1]}] !')
143        
144        # ensure we have a single column
145        rawT    = np.reshape(T     , -1)  # passing -1 creates a 1d array
146        values  = np.reshape(values, -1)
147
148        # interpolate values to gridT respecting gridskip
149        rawValues = values
150        temperatures = self.Grid.gridT[::gridskip]
151        interp = interpolate.PchipInterpolator(rawT, rawValues, extrapolate=False)
152        values = interp(temperatures)
153        values = np.where(np.isnan(values), 0, values)   # replace NaN by 0
154        
155        # add measurements
156        self.Measurements.appendMeasurement(rawT, temperatures, rawValues, values, heatrate, ID)

Imports measurements into FPEX0setup (fpex0.setup.Measurements). An interpolation to the integration grid is performed and the interpolated values are stored.

Takes

FPEX0setup
FPEX0_class_setup object.

ID
Name/identifier of experiment.

heatrate
Heating rate.

T
Temperature grid / measurement positions.

values
Measurement values (e.g. cp-values); where values[k] denotes the measurement value at T[k].

gridskip
Use grid with given stepsize (gridskip).
For gridskip=2 for example, only every second grid point is used, whereas gridskip=1 uses the whole grid.

Returns

No return value.

def importDSCobject(self, DSC_Data, gridskip=1):
159    def importDSCobject(self, DSC_Data, gridskip=1):
160        # T, values, heatrate, ID=None, gridskip=1
161
162        if type(DSC_Data) is list:
163            for i in range(len(DSC_Data)):
164                self.importDSCobject(DSC_Data[i], gridskip)
165            return
166
167        T = DSC_Data.T
168        # use cp data if available, raw dsc otherwise
169        if DSC_Data.cp is not None:
170            values = DSC_Data.cp
171        else: 
172            values = DSC_Data.dsc
173            print("WARNING: Using unprocessed dsc data. Make sure it is baseline-corrected!")
174        heatrate = DSC_Data.rate
175        ID = DSC_Data.ID
176
177        self.importMeasurements(T, values, heatrate, ID, gridskip)
class Parameters:
180class Parameters:
181    """
182    ## Parameters
183
184    **p0**
185    <br> Full initial parameter vector.
186
187    **p_lb**
188    <br> Full parameter lower bound vector.
189    
190    **p_ub**
191    <br> Full parameter upper bound vector.
192
193    **p0_FPdrift**
194    <br> Initial drift parameter vector.
195
196    **p0_FPdiffusion**
197    <br> Initial diffusion parameter vector.
198    
199    **p0_iniDist**
200    <br> Initial paramater vector for initial distribution.
201    
202    **p_lb_FPdrift**
203    <br> Lower bound vector for drift parameters.
204
205    **p_lb_FPdiffusion**
206    <br> ...
207    
208    **p_lb_iniDist**
209    <br> ...
210    
211    **p_ub_FPdrift**
212    <br> Upper bound vector for drift parameters.
213    
214    **p_ub_FPdiffusion**
215    <br> ...
216    
217    **p_ub_iniDist**
218    <br> ...
219
220    **idxFPdrift**
221    <br> Indices of drift parameters in p0 (resp. p_lb, p_ub).
222
223    **idxFPdiffusion**
224    <br> Same thing, diffusion parameters. 
225    
226    **idxFPall**
227    <br> Same thing, all Fokker-Planck parameters.
228    
229    **idxIniDist**
230    <br> Same thing, initial distribution parameters.
231    """
232    def __init__(self,  p0_FPdrift,        p0_FPdiffusion,        p0_iniDist,
233                        p_lb_FPdrift=None, p_lb_FPdiffusion=None, p_lb_iniDist=None,
234                        p_ub_FPdrift=None, p_ub_FPdiffusion=None, p_ub_iniDist=None ):
235
236        # ensure matching lengths
237        assert(p_lb_FPdrift is None or len(p0_FPdrift) == len(p_lb_FPdrift) 
238           and p_ub_FPdrift is None or len(p0_FPdrift) == len(p_ub_FPdrift))
239
240        assert(p_lb_FPdiffusion is None or len(p0_FPdiffusion) == len(p_lb_FPdiffusion) 
241           and p_ub_FPdiffusion is None or len(p0_FPdiffusion) == len(p_ub_FPdiffusion))
242
243        assert(p_lb_iniDist is None or len(p0_iniDist) == len(p_lb_iniDist) 
244           and p_ub_iniDist is None or len(p0_iniDist) == len(p_ub_iniDist))
245
246        # initial parameters
247        self.p0_FPdrift         = p0_FPdrift
248        self.p0_FPdiffusion     = p0_FPdiffusion
249        self.p0_iniDist         = p0_iniDist
250
251        # lower parameter bounds
252        # set to vectors of -np.inf if not given
253        if p_lb_FPdrift is None:
254            self.p_lb_FPdrift = np.full(len(p0_FPdrift), -np.inf)
255        else:
256            self.p_lb_FPdrift = p_lb_FPdrift
257
258        if p_lb_FPdiffusion is None:
259            self.p_lb_FPdiffusion = np.full(len(p0_FPdiffusion), -np.inf)
260        else:
261            self.p_lb_FPdiffusion = p_lb_FPdiffusion
262
263        if p_lb_iniDist is None:
264            self.p_lb_iniDist = np.full(len(p0_iniDist), -np.inf)
265        else:
266            self.p_lb_iniDist = p_lb_iniDist
267
268        # upper parameter bounds
269        # set to vectors of np.inf if not given
270        if p_ub_FPdrift is None:
271            self.p_ub_FPdrift = np.full(len(p0_FPdrift), np.inf)
272        else:
273            self.p_ub_FPdrift = p_ub_FPdrift
274
275        if p_ub_FPdiffusion is None:
276            self.p_ub_FPdiffusion = np.full(len(p0_FPdiffusion), np.inf)
277        else:
278            self.p_ub_FPdiffusion   = p_ub_FPdiffusion
279
280        if p_ub_iniDist is None:
281            self.p_ub_iniDist = np.full(len(p0_iniDist), np.inf)
282        else:
283            self.p_ub_iniDist       = p_ub_iniDist
284
285        # put initials, lower and upper bounds in one vector each
286        self.p0 = np.concatenate((p0_FPdrift, p0_FPdiffusion, p0_iniDist))
287        self.p_lb = np.concatenate((p_lb_FPdrift, p_lb_FPdiffusion, p_lb_iniDist))
288        self.p_ub = np.concatenate((p_ub_FPdrift, p_ub_FPdiffusion, p_ub_iniDist))
289        
290        # indices
291        self.idxFPdrift      = np.arange(0, len(p0_FPdrift) )
292        self.idxFPdiffusion  = np.arange(0, len(p0_FPdiffusion) ) + len(p0_FPdrift)
293        self.idxFPall        = np.arange(0, len(p0_FPdrift)+len(p0_FPdiffusion) )
294        self.idxIniDist      = np.arange(0, len(p0_iniDist) ) + ( len(p0_FPdrift) + len(p0_FPdiffusion) )
295
296
297    # extractor functions for external parameter vector (alike self.p0)
298    def extract_p_all(self, pvec):
299        return pvec
300
301    def extract_p_FPdrift(self, pvec):
302        return pvec[self.idxFPdrift]
303
304    def extract_p_FPdiffusion(self, pvec):
305        return pvec[self.idxFPdiffusion]
306
307    def extract_p_FPall(self, pvec):
308        return pvec[self.idxFPall]
309
310    def extract_p_iniDist(self, pvec):
311        return pvec[self.idxIniDist]
312
313
314    # setter functions (TODO: Set parameters in the vectors.)
315    def set_p0(self, p0):
316        self.p0 = p0
317
318    def set_p_lb(self, p_lb):
319        assert(len(p_lb) == len(self.p0))
320        self.p_lb = p_lb
321    
322    def set_p_ub(self, p_ub):
323        assert(len(p_ub) == len(self.p0))
324        self.p_ub = p_ub

Parameters

p0
Full initial parameter vector.

p_lb
Full parameter lower bound vector.

p_ub
Full parameter upper bound vector.

p0_FPdrift
Initial drift parameter vector.

p0_FPdiffusion
Initial diffusion parameter vector.

p0_iniDist
Initial paramater vector for initial distribution.

p_lb_FPdrift
Lower bound vector for drift parameters.

p_lb_FPdiffusion
...

p_lb_iniDist
...

p_ub_FPdrift
Upper bound vector for drift parameters.

p_ub_FPdiffusion
...

p_ub_iniDist
...

idxFPdrift
Indices of drift parameters in p0 (resp. p_lb, p_ub).

idxFPdiffusion
Same thing, diffusion parameters.

idxFPall
Same thing, all Fokker-Planck parameters.

idxIniDist
Same thing, initial distribution parameters.

Parameters( p0_FPdrift, p0_FPdiffusion, p0_iniDist, p_lb_FPdrift=None, p_lb_FPdiffusion=None, p_lb_iniDist=None, p_ub_FPdrift=None, p_ub_FPdiffusion=None, p_ub_iniDist=None)
232    def __init__(self,  p0_FPdrift,        p0_FPdiffusion,        p0_iniDist,
233                        p_lb_FPdrift=None, p_lb_FPdiffusion=None, p_lb_iniDist=None,
234                        p_ub_FPdrift=None, p_ub_FPdiffusion=None, p_ub_iniDist=None ):
235
236        # ensure matching lengths
237        assert(p_lb_FPdrift is None or len(p0_FPdrift) == len(p_lb_FPdrift) 
238           and p_ub_FPdrift is None or len(p0_FPdrift) == len(p_ub_FPdrift))
239
240        assert(p_lb_FPdiffusion is None or len(p0_FPdiffusion) == len(p_lb_FPdiffusion) 
241           and p_ub_FPdiffusion is None or len(p0_FPdiffusion) == len(p_ub_FPdiffusion))
242
243        assert(p_lb_iniDist is None or len(p0_iniDist) == len(p_lb_iniDist) 
244           and p_ub_iniDist is None or len(p0_iniDist) == len(p_ub_iniDist))
245
246        # initial parameters
247        self.p0_FPdrift         = p0_FPdrift
248        self.p0_FPdiffusion     = p0_FPdiffusion
249        self.p0_iniDist         = p0_iniDist
250
251        # lower parameter bounds
252        # set to vectors of -np.inf if not given
253        if p_lb_FPdrift is None:
254            self.p_lb_FPdrift = np.full(len(p0_FPdrift), -np.inf)
255        else:
256            self.p_lb_FPdrift = p_lb_FPdrift
257
258        if p_lb_FPdiffusion is None:
259            self.p_lb_FPdiffusion = np.full(len(p0_FPdiffusion), -np.inf)
260        else:
261            self.p_lb_FPdiffusion = p_lb_FPdiffusion
262
263        if p_lb_iniDist is None:
264            self.p_lb_iniDist = np.full(len(p0_iniDist), -np.inf)
265        else:
266            self.p_lb_iniDist = p_lb_iniDist
267
268        # upper parameter bounds
269        # set to vectors of np.inf if not given
270        if p_ub_FPdrift is None:
271            self.p_ub_FPdrift = np.full(len(p0_FPdrift), np.inf)
272        else:
273            self.p_ub_FPdrift = p_ub_FPdrift
274
275        if p_ub_FPdiffusion is None:
276            self.p_ub_FPdiffusion = np.full(len(p0_FPdiffusion), np.inf)
277        else:
278            self.p_ub_FPdiffusion   = p_ub_FPdiffusion
279
280        if p_ub_iniDist is None:
281            self.p_ub_iniDist = np.full(len(p0_iniDist), np.inf)
282        else:
283            self.p_ub_iniDist       = p_ub_iniDist
284
285        # put initials, lower and upper bounds in one vector each
286        self.p0 = np.concatenate((p0_FPdrift, p0_FPdiffusion, p0_iniDist))
287        self.p_lb = np.concatenate((p_lb_FPdrift, p_lb_FPdiffusion, p_lb_iniDist))
288        self.p_ub = np.concatenate((p_ub_FPdrift, p_ub_FPdiffusion, p_ub_iniDist))
289        
290        # indices
291        self.idxFPdrift      = np.arange(0, len(p0_FPdrift) )
292        self.idxFPdiffusion  = np.arange(0, len(p0_FPdiffusion) ) + len(p0_FPdrift)
293        self.idxFPall        = np.arange(0, len(p0_FPdrift)+len(p0_FPdiffusion) )
294        self.idxIniDist      = np.arange(0, len(p0_iniDist) ) + ( len(p0_FPdrift) + len(p0_FPdiffusion) )
def extract_p_all(self, pvec):
298    def extract_p_all(self, pvec):
299        return pvec
def extract_p_FPdrift(self, pvec):
301    def extract_p_FPdrift(self, pvec):
302        return pvec[self.idxFPdrift]
def extract_p_FPdiffusion(self, pvec):
304    def extract_p_FPdiffusion(self, pvec):
305        return pvec[self.idxFPdiffusion]
def extract_p_FPall(self, pvec):
307    def extract_p_FPall(self, pvec):
308        return pvec[self.idxFPall]
def extract_p_iniDist(self, pvec):
310    def extract_p_iniDist(self, pvec):
311        return pvec[self.idxIniDist]
def set_p0(self, p0):
315    def set_p0(self, p0):
316        self.p0 = p0
def set_p_lb(self, p_lb):
318    def set_p_lb(self, p_lb):
319        assert(len(p_lb) == len(self.p0))
320        self.p_lb = p_lb
def set_p_ub(self, p_ub):
322    def set_p_ub(self, p_ub):
323        assert(len(p_ub) == len(self.p0))
324        self.p_ub = p_ub
class Grid:
328class Grid:
329    """
330    FPEX0 discretization(?) grid.
331
332    ## Parameters
333    **gridT**
334    <br> x-grid = temperatures
335    <br> This is our method of lines discretization grid.
336    
337    **gridTdot**
338    <br> t-grid = heating rates
339    
340    **N** 
341    <br> Number of grid points.
342    
343    **h** 
344    <br> Grid size.
345    """
346    def __init__(self, gridT, gridTdot):
347        self.gridT = gridT
348        self.gridTdot = gridTdot
349        self.N = len(gridT)
350        self.h = ( gridT[-1] - gridT[0] ) / self.N

FPEX0 discretization(?) grid.

Parameters

gridT
x-grid = temperatures
This is our method of lines discretization grid.

gridTdot
t-grid = heating rates

N
Number of grid points.

h
Grid size.

Grid(gridT, gridTdot)
346    def __init__(self, gridT, gridTdot):
347        self.gridT = gridT
348        self.gridTdot = gridTdot
349        self.N = len(gridT)
350        self.h = ( gridT[-1] - gridT[0] ) / self.N
class Measurements:
354class Measurements:
355    """
356    Stores measurement data in list containers.
357    
358    ## Parameters
359     **rawT** 
360     <br> Measurement temperatures.
361     
362     **temperatures** 
363     <br> Selected temperatures from temperature grid.
364     
365     **rawValues** 
366     <br> Raw masurement values.
367     
368     **values** 
369     <br> rawValues interpolated to grid.
370     
371     **rates** 
372     <br> Heat rates.
373     
374     **ID** 
375     <br> Experiment name/identifier.
376    """
377    def __init__(self, rawT=[], temperatures=[], rawValues=[], values=[], rates=[], ID=[]):
378        self.rawT           = rawT
379        self.temperatures   = temperatures
380        self.rawValues      = rawValues
381        self.rates          = rates
382        self.ID             = ID
383        self.values         = values
384
385    def appendMeasurement(self, new_T, new_temperatures, new_rawValues, new_values, new_rate, new_ID=None):
386        """
387        Appends given measurements to the measurement parameters. 
388        """
389        self.rawT.append(new_T)
390        self.temperatures.append(new_temperatures)
391        self.rawValues.append(new_rawValues)
392        self.values.append(new_values)
393        self.rates.append(new_rate)
394        self.ID.append(new_ID)

Stores measurement data in list containers.

Parameters

rawT
Measurement temperatures.

temperatures
Selected temperatures from temperature grid.

rawValues
Raw masurement values.

values
rawValues interpolated to grid.

rates
Heat rates.

ID
Experiment name/identifier.

Measurements(rawT=[], temperatures=[], rawValues=[], values=[], rates=[], ID=[])
377    def __init__(self, rawT=[], temperatures=[], rawValues=[], values=[], rates=[], ID=[]):
378        self.rawT           = rawT
379        self.temperatures   = temperatures
380        self.rawValues      = rawValues
381        self.rates          = rates
382        self.ID             = ID
383        self.values         = values
def appendMeasurement( self, new_T, new_temperatures, new_rawValues, new_values, new_rate, new_ID=None):
385    def appendMeasurement(self, new_T, new_temperatures, new_rawValues, new_values, new_rate, new_ID=None):
386        """
387        Appends given measurements to the measurement parameters. 
388        """
389        self.rawT.append(new_T)
390        self.temperatures.append(new_temperatures)
391        self.rawValues.append(new_rawValues)
392        self.values.append(new_values)
393        self.rates.append(new_rate)
394        self.ID.append(new_ID)

Appends given measurements to the measurement parameters.

class Integration:
398class Integration:
399    """
400    Represents FPEX0 integrator.
401
402    ## Parameters
403    **method**
404    <br> Method to use by scipy solve_ivp. Default is BDF.
405    
406    **options**
407    <br> Integration options. Currently not used. 
408    
409    **integrator**
410    <br> Integrator for ODEs. Should have the signature integrator(FPrhs, t0tf, u0, method).
411
412    **NN, A, B**: Internal variables used by FokkerPlanckODE_dfdu.
413    """
414    # (TODO) Set default tolerances in Integration class (?)
415    def __init__(self, integrator=solve_ivp, method="BDF", options={}):
416        self.method     = method
417        self.options    = options
418        self.integrator = integrator
419        self.NN         = None  # variables for FokkerPlanckODE_dfdu()
420        self.A          = None
421        self.B          = None
422
423    def updateOptions(self, newOptions):
424        """ Returns the integrators options. """
425        options = copy(self.options)
426        for key in newOptions:
427            options[key] = newOptions[key]
428        self.options = options
429        return options
430    
431
432    def updateJacobian(self, jacobianFcn):
433        """ Returns an option dictionary with updated jacobian. """
434        options = copy(self.options)
435        options["jac"] = jacobianFcn
436        self.options = options
437        return options

Represents FPEX0 integrator.

Parameters

method
Method to use by scipy solve_ivp. Default is BDF.

options
Integration options. Currently not used.

integrator
Integrator for ODEs. Should have the signature integrator(FPrhs, t0tf, u0, method).

NN, A, B: Internal variables used by FokkerPlanckODE_dfdu.

Integration(integrator=<function solve_ivp>, method='BDF', options={})
415    def __init__(self, integrator=solve_ivp, method="BDF", options={}):
416        self.method     = method
417        self.options    = options
418        self.integrator = integrator
419        self.NN         = None  # variables for FokkerPlanckODE_dfdu()
420        self.A          = None
421        self.B          = None
def updateOptions(self, newOptions):
423    def updateOptions(self, newOptions):
424        """ Returns the integrators options. """
425        options = copy(self.options)
426        for key in newOptions:
427            options[key] = newOptions[key]
428        self.options = options
429        return options

Returns the integrators options.

def updateJacobian(self, jacobianFcn):
432    def updateJacobian(self, jacobianFcn):
433        """ Returns an option dictionary with updated jacobian. """
434        options = copy(self.options)
435        options["jac"] = jacobianFcn
436        self.options = options
437        return options

Returns an option dictionary with updated jacobian.