fpex0.setup API documentation

# fpex0.setup

View Source

      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: View Source

    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) View Source

    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: View Source

     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) View Source

    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): View Source

    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): View Source

     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): View Source

    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): View Source

    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: View Source

    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) View Source

    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): View Source

    298    def extract_p_all(self, pvec):
    299        return pvec

def extract_p_FPdrift(self, pvec): View Source

    301    def extract_p_FPdrift(self, pvec):
    302        return pvec[self.idxFPdrift]

def extract_p_FPdiffusion(self, pvec): View Source

    304    def extract_p_FPdiffusion(self, pvec):
    305        return pvec[self.idxFPdiffusion]

def extract_p_FPall(self, pvec): View Source

    307    def extract_p_FPall(self, pvec):
    308        return pvec[self.idxFPall]

def extract_p_iniDist(self, pvec): View Source

    310    def extract_p_iniDist(self, pvec):
    311        return pvec[self.idxIniDist]

def set_p0(self, p0): View Source

    315    def set_p0(self, p0):
    316        self.p0 = p0

def set_p_lb(self, p_lb): View Source

    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): View Source

    322    def set_p_ub(self, p_ub):
    323        assert(len(p_ub) == len(self.p0))
    324        self.p_ub = p_ub

class Grid: View Source

    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) View Source

    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: View Source

    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=[]) View Source

    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): View Source

    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: View Source

    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={}) View Source

    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): View Source

    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): View Source

    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.