Skip to content

Physical Properties (polykin.properties)¤

This module implements methods to evaluate physical property equations and estimate physical properties.

Antoine ¤

Antoine equation for vapor pressure.

This equation implements the following temperature dependence:

\[ \log_{base} P^* = A - \frac{B}{T + C} \]

where \(A\), \(B\) and \(C\) are component-specific constants, \(P^*\) is the vapor pressure and \(T\) is the temperature. When \(C=0\), this equation reverts to the Clapeyron equation.

Note

There is no consensus on the value of \(base\), the unit of temperature, or the unit of pressure. The function is flexible enough to accomodate most cases, but care should be taken to ensure the parameters match the intended use.

PARAMETER DESCRIPTION
A

Parameter of equation.

TYPE: float

B

Parameter of equation. Unit = K.

TYPE: float

C

Parameter of equation. Unit = K.

TYPE: float DEFAULT: 0.0

base10

If True base of logarithm is 10, otherwise it is \(e\).

TYPE: bool DEFAULT: True

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

unit

Unit of vapor pressure.

TYPE: str DEFAULT: 'Pa'

symbol

Symbol of vapor pressure.

TYPE: str DEFAULT: 'P^*'

name

Name.

TYPE: str DEFAULT: ''

See also
  • DIPPR101: alternative method, applicable to wider temperature ranges.
  • Wagner: alternative method, applicable to wider temperature ranges.
Source code in src/polykin/properties/equations/vapor_pressure.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
class Antoine(PropertyEquationT):
    r"""[Antoine](https://en.wikipedia.org/wiki/Antoine_equation) equation for
    vapor pressure.

    This equation implements the following temperature dependence:

    $$ \log_{base} P^* = A - \frac{B}{T + C} $$

    where $A$, $B$ and $C$ are component-specific constants, $P^*$ is the vapor
    pressure and $T$ is the temperature. When $C=0$, this equation reverts to
    the Clapeyron equation.

    !!! note
        There is no consensus on the value of $base$, the unit of temperature,
        or the unit of pressure. The function is flexible enough to accomodate
        most cases, but care should be taken to ensure the parameters match the
        intended use.

    Parameters
    ----------
    A : float
        Parameter of equation.
    B : float
        Parameter of equation.
        Unit = K.
    C : float
        Parameter of equation.
        Unit = K.
    base10 : bool
        If `True` base of logarithm is `10`, otherwise it is $e$.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    unit : str
        Unit of vapor pressure.
    symbol : str
        Symbol of vapor pressure.
    name : str
        Name.

    See also
    --------
    * [`DIPPR101`](./#polykin.properties.equations.dippr.DIPPR101):
      alternative method, applicable to wider temperature ranges.
    * [`Wagner`](./#polykin.properties.equations.vapor_pressure.Wagner):
      alternative method, applicable to wider temperature ranges.

    """

    _pinfo = {'A': ('', True), 'B': ('K', True), 'C': ('K', True),
              'base10': ('', False)}

    def __init__(self,
                 A: float,
                 B: float,
                 C: float = 0.,
                 base10: bool = True,
                 Tmin: float = 0.0,
                 Tmax: float = np.inf,
                 unit: str = 'Pa',
                 symbol: str = 'P^*',
                 name: str = ''
                 ) -> None:

        self.p = {'A': A, 'B': B, 'C': C, 'base10': base10}
        super().__init__((Tmin, Tmax), unit, symbol, name)

    @staticmethod
    def equation(T: Union[float, FloatArray],
                 A: float,
                 B: float,
                 C: float,
                 base10: bool
                 ) -> Union[float, FloatArray]:
        r"""Antoine equation.

        Parameters
        ----------
        T : float | FloatArray
            Temperature.
            Unit = K.
        A : float
            Parameter of equation.
        B : float
            Parameter of equation.
        C : float
            Parameter of equation.
        base10 : bool
            If `True` base of logarithm is `10`, otherwise it is $e$.

        Returns
        -------
        float | FloatArray
            Vapor pressure. Unit = Any.
        """
        x = A - B/(T + C)
        if base10:
            return 10**x
        else:
            return exp(x)

__call__ ¤

__call__(
    T: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
) -> Union[float, FloatArray]

Evaluate property equation at given temperature, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

RETURNS DESCRIPTION
float | FloatArray

Correlation value.

Source code in src/polykin/properties/equations/base.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __call__(self,
             T: Union[float, FloatArrayLike],
             Tunit: Literal['C', 'K'] = 'K'
             ) -> Union[float, FloatArray]:
    r"""Evaluate property equation at given temperature, including unit
    conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    Tunit : Literal['C', 'K']
        Temperature unit.

    Returns
    -------
    float | FloatArray
        Correlation value.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    return self.equation(TK, **self.p)

equation staticmethod ¤

equation(
    T: Union[float, FloatArray],
    A: float,
    B: float,
    C: float,
    base10: bool,
) -> Union[float, FloatArray]

Antoine equation.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

A

Parameter of equation.

TYPE: float

B

Parameter of equation.

TYPE: float

C

Parameter of equation.

TYPE: float

base10

If True base of logarithm is 10, otherwise it is \(e\).

TYPE: bool

RETURNS DESCRIPTION
float | FloatArray

Vapor pressure. Unit = Any.

Source code in src/polykin/properties/equations/vapor_pressure.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
@staticmethod
def equation(T: Union[float, FloatArray],
             A: float,
             B: float,
             C: float,
             base10: bool
             ) -> Union[float, FloatArray]:
    r"""Antoine equation.

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    A : float
        Parameter of equation.
    B : float
        Parameter of equation.
    C : float
        Parameter of equation.
    base10 : bool
        If `True` base of logarithm is `10`, otherwise it is $e$.

    Returns
    -------
    float | FloatArray
        Vapor pressure. Unit = Any.
    """
    x = A - B/(T + C)
    if base10:
        return 10**x
    else:
        return exp(x)

fit ¤

fit(
    T: FloatVectorLike,
    Y: FloatVectorLike,
    sigmaY: Optional[FloatVectorLike] = None,
    fit_only: Optional[list[str]] = None,
    logY: bool = False,
    plot: bool = True,
) -> dict

Fit equation to data using non-linear regression.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: FloatVector

Y

Property to be fitted. Unit = Any.

TYPE: FloatVector

sigmaY

Standard deviation of Y. Unit = [Y].

TYPE: FloatVector | None DEFAULT: None

fit_only

List with name of parameters to be fitted.

TYPE: list[str] | None DEFAULT: None

logY

If True, the fit will be done in terms of log(Y).

TYPE: bool DEFAULT: False

plot

If True a plot comparing data and fitted correlation will be generated.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
dict

A dictionary of results with the following keys: 'success', 'parameters', 'covariance', and 'plot'.

Source code in src/polykin/properties/equations/base.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def fit(self,
        T: FloatVectorLike,
        Y: FloatVectorLike,
        sigmaY: Optional[FloatVectorLike] = None,
        fit_only: Optional[list[str]] = None,
        logY: bool = False,
        plot: bool = True,
        ) -> dict:
    """Fit equation to data using non-linear regression.

    Parameters
    ----------
    T : FloatVector
        Temperature. Unit = K.
    Y : FloatVector
        Property to be fitted. Unit = Any.
    sigmaY : FloatVector | None
        Standard deviation of Y. Unit = [Y].
    fit_only : list[str] | None
        List with name of parameters to be fitted.
    logY : bool
        If `True`, the fit will be done in terms of log(Y).
    plot : bool
        If `True` a plot comparing data and fitted correlation will be
        generated.

    Returns
    -------
    dict
        A dictionary of results with the following keys: 'success',
        'parameters', 'covariance', and 'plot'.
    """

    # Current parameter values
    pdict = self.p.copy()

    # Select parameters to be fitted
    pnames_fit = [name for name, info in self._pinfo.items() if info[1]]
    if fit_only:
        pnames_fit = set(fit_only) & set(pnames_fit)
    p0 = [pdict[pname] for pname in pnames_fit]

    # Fit function
    def ffit(x, *p):
        for pname, pvalue in zip(pnames_fit, p):
            pdict[pname] = pvalue
        Yfit = self.equation(T=x, **pdict)
        if logY:
            Yfit = log(Yfit)
        return Yfit

    solution = curve_fit(ffit,
                         xdata=T,
                         ydata=log(Y) if logY else Y,
                         p0=p0,
                         sigma=sigmaY,
                         absolute_sigma=False,
                         full_output=True)
    result = {}
    result['success'] = bool(solution[4])
    if solution[4]:
        popt = solution[0]
        pcov = solution[1]
        print("Fit successful.")
        for pname, pvalue in zip(pnames_fit, popt):
            print(f"{pname}: {pvalue}")
        print("Covariance:")
        print(pcov)
        result['covariance'] = pcov

        # Update attributes
        self.Trange = (min(T), max(T))
        for pname, pvalue in zip(pnames_fit, popt):
            self.p[pname] = pvalue
        result['parameters'] = pdict

        # plot
        if plot:
            kind = 'semilogy' if logY else 'linear'
            fig, ax = self.plot(kind=kind, return_objects=True)  # ok
            ax.plot(T, Y, 'o', mfc='none')
            result['plot'] = (fig, ax)
    else:
        print("Fit error: ", solution[3])
        result['message'] = solution[3]

    return result

plot ¤

plot(
    kind: Literal[
        "linear", "semilogy", "Arrhenius"
    ] = "linear",
    Trange: Optional[tuple[float, float]] = None,
    Tunit: Literal["C", "K"] = "K",
    title: Optional[str] = None,
    axes: Optional[Axes] = None,
    return_objects: bool = False,
) -> Optional[tuple[Optional[Figure], Axes]]

Plot quantity as a function of temperature.

PARAMETER DESCRIPTION
kind

Kind of plot to be generated.

TYPE: Literal['linear', 'semilogy', 'Arrhenius'] DEFAULT: 'linear'

Trange

Temperature range for x-axis. If None, the validity range (Tmin, Tmax) will be used. If no validity range was defined, the range will default to 0-100°C.

TYPE: tuple[float, float] | None DEFAULT: None

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

title

Title of plot. If None, the object name will be used.

TYPE: str | None DEFAULT: None

axes

Matplotlib Axes object.

TYPE: Axes | None DEFAULT: None

return_objects

If True, the Figure and Axes objects are returned (for saving or further manipulations).

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
tuple[Figure | None, Axes] | None

Figure and Axes objects if return_objects is True.

Source code in src/polykin/properties/equations/base.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def plot(self,
         kind: Literal['linear', 'semilogy', 'Arrhenius'] = 'linear',
         Trange: Optional[tuple[float, float]] = None,
         Tunit: Literal['C', 'K'] = 'K',
         title: Optional[str] = None,
         axes: Optional[Axes] = None,
         return_objects: bool = False
         ) -> Optional[tuple[Optional[Figure], Axes]]:
    """Plot quantity as a function of temperature.

    Parameters
    ----------
    kind : Literal['linear', 'semilogy', 'Arrhenius']
        Kind of plot to be generated.
    Trange : tuple[float, float] | None
        Temperature range for x-axis. If `None`, the validity range
        (Tmin, Tmax) will be used. If no validity range was defined, the
        range will default to 0-100°C.
    Tunit : Literal['C', 'K']
        Temperature unit.
    title : str | None
        Title of plot. If `None`, the object name will be used.
    axes : Axes | None
        Matplotlib Axes object.
    return_objects : bool
        If `True`, the Figure and Axes objects are returned (for saving or
        further manipulations).

    Returns
    -------
    tuple[Figure | None, Axes] | None
        Figure and Axes objects if return_objects is `True`.
    """

    # Check inputs
    check_in_set(kind, {'linear', 'semilogy', 'Arrhenius'}, 'kind')
    check_in_set(Tunit, {'K', 'C'}, 'Tunit')
    if Trange is not None:
        Trange_min = 0.
        if Tunit == 'C':
            Trange_min = -273.15
        check_valid_range(Trange, Trange_min, np.inf, 'Trange')

    # Plot objects
    if axes is None:
        fig, ax = plt.subplots()
        if title is None:
            title = self.name
        if title:
            fig.suptitle(title)
        label = None
    else:
        fig = None
        ax = axes
        label = self.name

    # Units and xlabel
    Tunit_range = Tunit
    if kind == 'Arrhenius':
        Tunit = 'K'
    Tsymbol = Tunit
    if Tunit == 'C':
        Tsymbol = '°' + Tunit

    if kind == 'Arrhenius':
        xlabel = r"$1/T$ [" + Tsymbol + r"$^{-1}$]"
    else:
        xlabel = fr"$T$ [{Tsymbol}]"

    # ylabel
    ylabel = fr"${self.symbol}$ [{self.unit}]"
    if axes is not None:
        ylabel0 = ax.get_ylabel()
        if ylabel0 and ylabel not in ylabel0:
            ylabel = ylabel0 + ", " + ylabel

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True)

    # x-axis vector
    if Trange is not None:
        if Tunit_range == 'C':
            Trange = (Trange[0]+273.15, Trange[1]+273.15)
    else:
        Trange = (np.min(self.Trange[0]), np.max(self.Trange[1]))
        if Trange == (0.0, np.inf):
            Trange = (273.15, 373.15)

    try:
        shape = self._shape
    except AttributeError:
        shape = None
    if shape is not None:
        print("Plot method not yet implemented for array-like equations.")
    else:
        TK = np.linspace(*Trange, 100)
        y = self.__call__(TK, 'K')
        T = TK
        if Tunit == 'C':
            T -= 273.15
        if kind == 'linear':
            ax.plot(T, y, label=label)
        elif kind == 'semilogy':
            ax.semilogy(T, y, label=label)
        elif kind == 'Arrhenius':
            ax.semilogy(1/TK, y, label=label)

    if fig is None:
        ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")

    if return_objects:
        return (fig, ax)

DIPPR100 ¤

DIPPR-100 equation.

This equation implements the following temperature dependence:

\[ Y = A + B T + C T^2 + D T^3 + E T^4 \]

where \(A\) to \(E\) are component-specific constants and \(T\) is the absolute temperature.

PARAMETER DESCRIPTION
A

Parameter of equation.

TYPE: float DEFAULT: 0.0

B

Parameter of equation.

TYPE: float DEFAULT: 0.0

C

Parameter of equation.

TYPE: float DEFAULT: 0.0

D

Parameter of equation.

TYPE: float DEFAULT: 0.0

E

Parameter of equation.

TYPE: float DEFAULT: 0.0

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

unit

Unit of output variable \(Y\).

TYPE: str DEFAULT: '-'

symbol

Symbol of output variable \(Y\).

TYPE: str DEFAULT: 'Y'

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/equations/dippr.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
class DIPPR100(DIPPRP5):
    r"""[DIPPR](https://de.wikipedia.org/wiki/DIPPR-Gleichungen)-100 equation.

    This equation implements the following temperature dependence:

    $$ Y = A + B T + C T^2 + D T^3 + E T^4 $$

    where $A$ to $E$ are component-specific constants and $T$ is the absolute
    temperature.

    Parameters
    ----------
    A : float
        Parameter of equation.
    B : float
        Parameter of equation.
    C : float
        Parameter of equation.
    D : float
        Parameter of equation.
    E : float
        Parameter of equation.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    unit : str
        Unit of output variable $Y$.
    symbol : str
        Symbol of output variable $Y$.
    name : str
        Name.
    """

    _pinfo = {'A': ('#', True), 'B': ('#/K', True), 'C': ('#/K²', True),
              'D': ('#/K³', True), 'E': ('#/K⁴', True)}

    def __init__(self,
                 A: float = 0.,
                 B: float = 0.,
                 C: float = 0.,
                 D: float = 0.,
                 E: float = 0.,
                 Tmin: float = 0.0,
                 Tmax: float = np.inf,
                 unit: str = '-',
                 symbol: str = 'Y',
                 name: str = ''
                 ) -> None:

        super().__init__(A, B, C, D, E, Tmin, Tmax, unit, symbol, name)

    @staticmethod
    def equation(T: Union[float, FloatArray],
                 A: float,
                 B: float,
                 C: float,
                 D: float,
                 E: float
                 ) -> Union[float, FloatArray]:
        r"""DIPPR-100 equation."""
        return A + B*T + C*T**2 + D*T**3 + E*T**4

__call__ ¤

__call__(
    T: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
) -> Union[float, FloatArray]

Evaluate property equation at given temperature, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

RETURNS DESCRIPTION
float | FloatArray

Correlation value.

Source code in src/polykin/properties/equations/base.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __call__(self,
             T: Union[float, FloatArrayLike],
             Tunit: Literal['C', 'K'] = 'K'
             ) -> Union[float, FloatArray]:
    r"""Evaluate property equation at given temperature, including unit
    conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    Tunit : Literal['C', 'K']
        Temperature unit.

    Returns
    -------
    float | FloatArray
        Correlation value.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    return self.equation(TK, **self.p)

equation staticmethod ¤

equation(
    T: Union[float, FloatArray],
    A: float,
    B: float,
    C: float,
    D: float,
    E: float,
) -> Union[float, FloatArray]

DIPPR-100 equation.

Source code in src/polykin/properties/equations/dippr.py
125
126
127
128
129
130
131
132
133
134
@staticmethod
def equation(T: Union[float, FloatArray],
             A: float,
             B: float,
             C: float,
             D: float,
             E: float
             ) -> Union[float, FloatArray]:
    r"""DIPPR-100 equation."""
    return A + B*T + C*T**2 + D*T**3 + E*T**4

fit ¤

fit(
    T: FloatVectorLike,
    Y: FloatVectorLike,
    sigmaY: Optional[FloatVectorLike] = None,
    fit_only: Optional[list[str]] = None,
    logY: bool = False,
    plot: bool = True,
) -> dict

Fit equation to data using non-linear regression.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: FloatVector

Y

Property to be fitted. Unit = Any.

TYPE: FloatVector

sigmaY

Standard deviation of Y. Unit = [Y].

TYPE: FloatVector | None DEFAULT: None

fit_only

List with name of parameters to be fitted.

TYPE: list[str] | None DEFAULT: None

logY

If True, the fit will be done in terms of log(Y).

TYPE: bool DEFAULT: False

plot

If True a plot comparing data and fitted correlation will be generated.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
dict

A dictionary of results with the following keys: 'success', 'parameters', 'covariance', and 'plot'.

Source code in src/polykin/properties/equations/base.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def fit(self,
        T: FloatVectorLike,
        Y: FloatVectorLike,
        sigmaY: Optional[FloatVectorLike] = None,
        fit_only: Optional[list[str]] = None,
        logY: bool = False,
        plot: bool = True,
        ) -> dict:
    """Fit equation to data using non-linear regression.

    Parameters
    ----------
    T : FloatVector
        Temperature. Unit = K.
    Y : FloatVector
        Property to be fitted. Unit = Any.
    sigmaY : FloatVector | None
        Standard deviation of Y. Unit = [Y].
    fit_only : list[str] | None
        List with name of parameters to be fitted.
    logY : bool
        If `True`, the fit will be done in terms of log(Y).
    plot : bool
        If `True` a plot comparing data and fitted correlation will be
        generated.

    Returns
    -------
    dict
        A dictionary of results with the following keys: 'success',
        'parameters', 'covariance', and 'plot'.
    """

    # Current parameter values
    pdict = self.p.copy()

    # Select parameters to be fitted
    pnames_fit = [name for name, info in self._pinfo.items() if info[1]]
    if fit_only:
        pnames_fit = set(fit_only) & set(pnames_fit)
    p0 = [pdict[pname] for pname in pnames_fit]

    # Fit function
    def ffit(x, *p):
        for pname, pvalue in zip(pnames_fit, p):
            pdict[pname] = pvalue
        Yfit = self.equation(T=x, **pdict)
        if logY:
            Yfit = log(Yfit)
        return Yfit

    solution = curve_fit(ffit,
                         xdata=T,
                         ydata=log(Y) if logY else Y,
                         p0=p0,
                         sigma=sigmaY,
                         absolute_sigma=False,
                         full_output=True)
    result = {}
    result['success'] = bool(solution[4])
    if solution[4]:
        popt = solution[0]
        pcov = solution[1]
        print("Fit successful.")
        for pname, pvalue in zip(pnames_fit, popt):
            print(f"{pname}: {pvalue}")
        print("Covariance:")
        print(pcov)
        result['covariance'] = pcov

        # Update attributes
        self.Trange = (min(T), max(T))
        for pname, pvalue in zip(pnames_fit, popt):
            self.p[pname] = pvalue
        result['parameters'] = pdict

        # plot
        if plot:
            kind = 'semilogy' if logY else 'linear'
            fig, ax = self.plot(kind=kind, return_objects=True)  # ok
            ax.plot(T, Y, 'o', mfc='none')
            result['plot'] = (fig, ax)
    else:
        print("Fit error: ", solution[3])
        result['message'] = solution[3]

    return result

plot ¤

plot(
    kind: Literal[
        "linear", "semilogy", "Arrhenius"
    ] = "linear",
    Trange: Optional[tuple[float, float]] = None,
    Tunit: Literal["C", "K"] = "K",
    title: Optional[str] = None,
    axes: Optional[Axes] = None,
    return_objects: bool = False,
) -> Optional[tuple[Optional[Figure], Axes]]

Plot quantity as a function of temperature.

PARAMETER DESCRIPTION
kind

Kind of plot to be generated.

TYPE: Literal['linear', 'semilogy', 'Arrhenius'] DEFAULT: 'linear'

Trange

Temperature range for x-axis. If None, the validity range (Tmin, Tmax) will be used. If no validity range was defined, the range will default to 0-100°C.

TYPE: tuple[float, float] | None DEFAULT: None

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

title

Title of plot. If None, the object name will be used.

TYPE: str | None DEFAULT: None

axes

Matplotlib Axes object.

TYPE: Axes | None DEFAULT: None

return_objects

If True, the Figure and Axes objects are returned (for saving or further manipulations).

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
tuple[Figure | None, Axes] | None

Figure and Axes objects if return_objects is True.

Source code in src/polykin/properties/equations/base.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def plot(self,
         kind: Literal['linear', 'semilogy', 'Arrhenius'] = 'linear',
         Trange: Optional[tuple[float, float]] = None,
         Tunit: Literal['C', 'K'] = 'K',
         title: Optional[str] = None,
         axes: Optional[Axes] = None,
         return_objects: bool = False
         ) -> Optional[tuple[Optional[Figure], Axes]]:
    """Plot quantity as a function of temperature.

    Parameters
    ----------
    kind : Literal['linear', 'semilogy', 'Arrhenius']
        Kind of plot to be generated.
    Trange : tuple[float, float] | None
        Temperature range for x-axis. If `None`, the validity range
        (Tmin, Tmax) will be used. If no validity range was defined, the
        range will default to 0-100°C.
    Tunit : Literal['C', 'K']
        Temperature unit.
    title : str | None
        Title of plot. If `None`, the object name will be used.
    axes : Axes | None
        Matplotlib Axes object.
    return_objects : bool
        If `True`, the Figure and Axes objects are returned (for saving or
        further manipulations).

    Returns
    -------
    tuple[Figure | None, Axes] | None
        Figure and Axes objects if return_objects is `True`.
    """

    # Check inputs
    check_in_set(kind, {'linear', 'semilogy', 'Arrhenius'}, 'kind')
    check_in_set(Tunit, {'K', 'C'}, 'Tunit')
    if Trange is not None:
        Trange_min = 0.
        if Tunit == 'C':
            Trange_min = -273.15
        check_valid_range(Trange, Trange_min, np.inf, 'Trange')

    # Plot objects
    if axes is None:
        fig, ax = plt.subplots()
        if title is None:
            title = self.name
        if title:
            fig.suptitle(title)
        label = None
    else:
        fig = None
        ax = axes
        label = self.name

    # Units and xlabel
    Tunit_range = Tunit
    if kind == 'Arrhenius':
        Tunit = 'K'
    Tsymbol = Tunit
    if Tunit == 'C':
        Tsymbol = '°' + Tunit

    if kind == 'Arrhenius':
        xlabel = r"$1/T$ [" + Tsymbol + r"$^{-1}$]"
    else:
        xlabel = fr"$T$ [{Tsymbol}]"

    # ylabel
    ylabel = fr"${self.symbol}$ [{self.unit}]"
    if axes is not None:
        ylabel0 = ax.get_ylabel()
        if ylabel0 and ylabel not in ylabel0:
            ylabel = ylabel0 + ", " + ylabel

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True)

    # x-axis vector
    if Trange is not None:
        if Tunit_range == 'C':
            Trange = (Trange[0]+273.15, Trange[1]+273.15)
    else:
        Trange = (np.min(self.Trange[0]), np.max(self.Trange[1]))
        if Trange == (0.0, np.inf):
            Trange = (273.15, 373.15)

    try:
        shape = self._shape
    except AttributeError:
        shape = None
    if shape is not None:
        print("Plot method not yet implemented for array-like equations.")
    else:
        TK = np.linspace(*Trange, 100)
        y = self.__call__(TK, 'K')
        T = TK
        if Tunit == 'C':
            T -= 273.15
        if kind == 'linear':
            ax.plot(T, y, label=label)
        elif kind == 'semilogy':
            ax.semilogy(T, y, label=label)
        elif kind == 'Arrhenius':
            ax.semilogy(1/TK, y, label=label)

    if fig is None:
        ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")

    if return_objects:
        return (fig, ax)

DIPPR101 ¤

DIPPR-101 equation.

This equation implements the following temperature dependence:

\[ Y = \exp{\left(A + B / T + C \ln(T) + D T^E\right)} \]

where \(A\) to \(E\) are component-specific constants and \(T\) is the absolute temperature.

PARAMETER DESCRIPTION
A

Parameter of equation.

TYPE: float

B

Parameter of equation.

TYPE: float

C

Parameter of equation.

TYPE: float DEFAULT: 0.0

D

Parameter of equation.

TYPE: float DEFAULT: 0.0

E

Parameter of equation.

TYPE: float DEFAULT: 0.0

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

unit

Unit of output variable \(Y\).

TYPE: str DEFAULT: '-'

symbol

Symbol of output variable \(Y\).

TYPE: str DEFAULT: 'Y'

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/equations/dippr.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
class DIPPR101(DIPPRP5):
    r"""[DIPPR](https://de.wikipedia.org/wiki/DIPPR-Gleichungen)-101 equation.

    This equation implements the following temperature dependence:

    $$ Y = \exp{\left(A + B / T + C \ln(T) + D T^E\right)} $$

    where $A$ to $E$ are component-specific constants and $T$ is the absolute
    temperature.

    Parameters
    ----------
    A : float
        Parameter of equation.
    B : float
        Parameter of equation.
    C : float
        Parameter of equation.
    D : float
        Parameter of equation.
    E : float
        Parameter of equation.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    unit : str
        Unit of output variable $Y$.
    symbol : str
        Symbol of output variable $Y$.
    name : str
        Name.
    """
    _pinfo = {'A': ('', True), 'B': ('K', True), 'C': ('', True),
              'D': ('', True), 'E': ('', True)}

    def __init__(self,
                 A: float,
                 B: float,
                 C: float = 0.,
                 D: float = 0.,
                 E: float = 0.,
                 Tmin: float = 0.0,
                 Tmax: float = np.inf,
                 unit: str = '-',
                 symbol: str = 'Y',
                 name: str = ''
                 ) -> None:

        super().__init__(A, B, C, D, E, Tmin, Tmax, unit, symbol, name)

    @staticmethod
    def equation(T: Union[float, FloatArray],
                 A: float,
                 B: float,
                 C: float,
                 D: float,
                 E: float
                 ) -> Union[float, FloatArray]:
        r"""DIPPR-101 equation."""
        return exp(A + B/T + C*log(T) + D*T**E)

__call__ ¤

__call__(
    T: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
) -> Union[float, FloatArray]

Evaluate property equation at given temperature, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

RETURNS DESCRIPTION
float | FloatArray

Correlation value.

Source code in src/polykin/properties/equations/base.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __call__(self,
             T: Union[float, FloatArrayLike],
             Tunit: Literal['C', 'K'] = 'K'
             ) -> Union[float, FloatArray]:
    r"""Evaluate property equation at given temperature, including unit
    conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    Tunit : Literal['C', 'K']
        Temperature unit.

    Returns
    -------
    float | FloatArray
        Correlation value.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    return self.equation(TK, **self.p)

equation staticmethod ¤

equation(
    T: Union[float, FloatArray],
    A: float,
    B: float,
    C: float,
    D: float,
    E: float,
) -> Union[float, FloatArray]

DIPPR-101 equation.

Source code in src/polykin/properties/equations/dippr.py
190
191
192
193
194
195
196
197
198
199
@staticmethod
def equation(T: Union[float, FloatArray],
             A: float,
             B: float,
             C: float,
             D: float,
             E: float
             ) -> Union[float, FloatArray]:
    r"""DIPPR-101 equation."""
    return exp(A + B/T + C*log(T) + D*T**E)

fit ¤

fit(
    T: FloatVectorLike,
    Y: FloatVectorLike,
    sigmaY: Optional[FloatVectorLike] = None,
    fit_only: Optional[list[str]] = None,
    logY: bool = False,
    plot: bool = True,
) -> dict

Fit equation to data using non-linear regression.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: FloatVector

Y

Property to be fitted. Unit = Any.

TYPE: FloatVector

sigmaY

Standard deviation of Y. Unit = [Y].

TYPE: FloatVector | None DEFAULT: None

fit_only

List with name of parameters to be fitted.

TYPE: list[str] | None DEFAULT: None

logY

If True, the fit will be done in terms of log(Y).

TYPE: bool DEFAULT: False

plot

If True a plot comparing data and fitted correlation will be generated.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
dict

A dictionary of results with the following keys: 'success', 'parameters', 'covariance', and 'plot'.

Source code in src/polykin/properties/equations/base.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def fit(self,
        T: FloatVectorLike,
        Y: FloatVectorLike,
        sigmaY: Optional[FloatVectorLike] = None,
        fit_only: Optional[list[str]] = None,
        logY: bool = False,
        plot: bool = True,
        ) -> dict:
    """Fit equation to data using non-linear regression.

    Parameters
    ----------
    T : FloatVector
        Temperature. Unit = K.
    Y : FloatVector
        Property to be fitted. Unit = Any.
    sigmaY : FloatVector | None
        Standard deviation of Y. Unit = [Y].
    fit_only : list[str] | None
        List with name of parameters to be fitted.
    logY : bool
        If `True`, the fit will be done in terms of log(Y).
    plot : bool
        If `True` a plot comparing data and fitted correlation will be
        generated.

    Returns
    -------
    dict
        A dictionary of results with the following keys: 'success',
        'parameters', 'covariance', and 'plot'.
    """

    # Current parameter values
    pdict = self.p.copy()

    # Select parameters to be fitted
    pnames_fit = [name for name, info in self._pinfo.items() if info[1]]
    if fit_only:
        pnames_fit = set(fit_only) & set(pnames_fit)
    p0 = [pdict[pname] for pname in pnames_fit]

    # Fit function
    def ffit(x, *p):
        for pname, pvalue in zip(pnames_fit, p):
            pdict[pname] = pvalue
        Yfit = self.equation(T=x, **pdict)
        if logY:
            Yfit = log(Yfit)
        return Yfit

    solution = curve_fit(ffit,
                         xdata=T,
                         ydata=log(Y) if logY else Y,
                         p0=p0,
                         sigma=sigmaY,
                         absolute_sigma=False,
                         full_output=True)
    result = {}
    result['success'] = bool(solution[4])
    if solution[4]:
        popt = solution[0]
        pcov = solution[1]
        print("Fit successful.")
        for pname, pvalue in zip(pnames_fit, popt):
            print(f"{pname}: {pvalue}")
        print("Covariance:")
        print(pcov)
        result['covariance'] = pcov

        # Update attributes
        self.Trange = (min(T), max(T))
        for pname, pvalue in zip(pnames_fit, popt):
            self.p[pname] = pvalue
        result['parameters'] = pdict

        # plot
        if plot:
            kind = 'semilogy' if logY else 'linear'
            fig, ax = self.plot(kind=kind, return_objects=True)  # ok
            ax.plot(T, Y, 'o', mfc='none')
            result['plot'] = (fig, ax)
    else:
        print("Fit error: ", solution[3])
        result['message'] = solution[3]

    return result

plot ¤

plot(
    kind: Literal[
        "linear", "semilogy", "Arrhenius"
    ] = "linear",
    Trange: Optional[tuple[float, float]] = None,
    Tunit: Literal["C", "K"] = "K",
    title: Optional[str] = None,
    axes: Optional[Axes] = None,
    return_objects: bool = False,
) -> Optional[tuple[Optional[Figure], Axes]]

Plot quantity as a function of temperature.

PARAMETER DESCRIPTION
kind

Kind of plot to be generated.

TYPE: Literal['linear', 'semilogy', 'Arrhenius'] DEFAULT: 'linear'

Trange

Temperature range for x-axis. If None, the validity range (Tmin, Tmax) will be used. If no validity range was defined, the range will default to 0-100°C.

TYPE: tuple[float, float] | None DEFAULT: None

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

title

Title of plot. If None, the object name will be used.

TYPE: str | None DEFAULT: None

axes

Matplotlib Axes object.

TYPE: Axes | None DEFAULT: None

return_objects

If True, the Figure and Axes objects are returned (for saving or further manipulations).

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
tuple[Figure | None, Axes] | None

Figure and Axes objects if return_objects is True.

Source code in src/polykin/properties/equations/base.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def plot(self,
         kind: Literal['linear', 'semilogy', 'Arrhenius'] = 'linear',
         Trange: Optional[tuple[float, float]] = None,
         Tunit: Literal['C', 'K'] = 'K',
         title: Optional[str] = None,
         axes: Optional[Axes] = None,
         return_objects: bool = False
         ) -> Optional[tuple[Optional[Figure], Axes]]:
    """Plot quantity as a function of temperature.

    Parameters
    ----------
    kind : Literal['linear', 'semilogy', 'Arrhenius']
        Kind of plot to be generated.
    Trange : tuple[float, float] | None
        Temperature range for x-axis. If `None`, the validity range
        (Tmin, Tmax) will be used. If no validity range was defined, the
        range will default to 0-100°C.
    Tunit : Literal['C', 'K']
        Temperature unit.
    title : str | None
        Title of plot. If `None`, the object name will be used.
    axes : Axes | None
        Matplotlib Axes object.
    return_objects : bool
        If `True`, the Figure and Axes objects are returned (for saving or
        further manipulations).

    Returns
    -------
    tuple[Figure | None, Axes] | None
        Figure and Axes objects if return_objects is `True`.
    """

    # Check inputs
    check_in_set(kind, {'linear', 'semilogy', 'Arrhenius'}, 'kind')
    check_in_set(Tunit, {'K', 'C'}, 'Tunit')
    if Trange is not None:
        Trange_min = 0.
        if Tunit == 'C':
            Trange_min = -273.15
        check_valid_range(Trange, Trange_min, np.inf, 'Trange')

    # Plot objects
    if axes is None:
        fig, ax = plt.subplots()
        if title is None:
            title = self.name
        if title:
            fig.suptitle(title)
        label = None
    else:
        fig = None
        ax = axes
        label = self.name

    # Units and xlabel
    Tunit_range = Tunit
    if kind == 'Arrhenius':
        Tunit = 'K'
    Tsymbol = Tunit
    if Tunit == 'C':
        Tsymbol = '°' + Tunit

    if kind == 'Arrhenius':
        xlabel = r"$1/T$ [" + Tsymbol + r"$^{-1}$]"
    else:
        xlabel = fr"$T$ [{Tsymbol}]"

    # ylabel
    ylabel = fr"${self.symbol}$ [{self.unit}]"
    if axes is not None:
        ylabel0 = ax.get_ylabel()
        if ylabel0 and ylabel not in ylabel0:
            ylabel = ylabel0 + ", " + ylabel

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True)

    # x-axis vector
    if Trange is not None:
        if Tunit_range == 'C':
            Trange = (Trange[0]+273.15, Trange[1]+273.15)
    else:
        Trange = (np.min(self.Trange[0]), np.max(self.Trange[1]))
        if Trange == (0.0, np.inf):
            Trange = (273.15, 373.15)

    try:
        shape = self._shape
    except AttributeError:
        shape = None
    if shape is not None:
        print("Plot method not yet implemented for array-like equations.")
    else:
        TK = np.linspace(*Trange, 100)
        y = self.__call__(TK, 'K')
        T = TK
        if Tunit == 'C':
            T -= 273.15
        if kind == 'linear':
            ax.plot(T, y, label=label)
        elif kind == 'semilogy':
            ax.semilogy(T, y, label=label)
        elif kind == 'Arrhenius':
            ax.semilogy(1/TK, y, label=label)

    if fig is None:
        ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")

    if return_objects:
        return (fig, ax)

DIPPR102 ¤

DIPPR-102 equation.

This equation implements the following temperature dependence:

\[ Y = \frac{A T^B}{ 1 + C/T + D/T^2} \]

where \(A\) to \(D\) are component-specific constants and \(T\) is the absolute temperature.

PARAMETER DESCRIPTION
A

Parameter of equation.

TYPE: float

B

Parameter of equation.

TYPE: float

C

Parameter of equation.

TYPE: float DEFAULT: 0.0

D

Parameter of equation.

TYPE: float DEFAULT: 0.0

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

unit

Unit of output variable \(Y\).

TYPE: str DEFAULT: '-'

symbol

Symbol of output variable \(Y\).

TYPE: str DEFAULT: 'Y'

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/equations/dippr.py
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
class DIPPR102(DIPPRP4):
    r"""[DIPPR](https://de.wikipedia.org/wiki/DIPPR-Gleichungen)-102 equation.

    This equation implements the following temperature dependence:

    $$ Y = \frac{A T^B}{ 1 + C/T + D/T^2} $$

    where $A$ to $D$ are component-specific constants and $T$ is the absolute
    temperature.

    Parameters
    ----------
    A : float
        Parameter of equation.
    B : float
        Parameter of equation.
    C : float
        Parameter of equation.
    D : float
        Parameter of equation.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    unit : str
        Unit of output variable $Y$.
    symbol : str
        Symbol of output variable $Y$.
    name : str
        Name.
    """

    _pinfo = {'A': ('#', True), 'B': ('', True), 'C': ('K', True),
              'D': ('K²', True)}

    def __init__(self,
                 A: float,
                 B: float,
                 C: float = 0.,
                 D: float = 0.,
                 Tmin: float = 0.0,
                 Tmax: float = np.inf,
                 unit: str = '-',
                 symbol: str = 'Y',
                 name: str = ''
                 ) -> None:

        super().__init__(A, B, C, D, Tmin, Tmax, unit, symbol, name)

    @staticmethod
    def equation(T: Union[float, FloatArray],
                 A: float,
                 B: float,
                 C: float,
                 D: float
                 ) -> Union[float, FloatArray]:
        r"""DIPPR-102 equation."""
        return (A * T**B) / (1 + C/T + D/T**2)

__call__ ¤

__call__(
    T: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
) -> Union[float, FloatArray]

Evaluate property equation at given temperature, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

RETURNS DESCRIPTION
float | FloatArray

Correlation value.

Source code in src/polykin/properties/equations/base.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __call__(self,
             T: Union[float, FloatArrayLike],
             Tunit: Literal['C', 'K'] = 'K'
             ) -> Union[float, FloatArray]:
    r"""Evaluate property equation at given temperature, including unit
    conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    Tunit : Literal['C', 'K']
        Temperature unit.

    Returns
    -------
    float | FloatArray
        Correlation value.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    return self.equation(TK, **self.p)

equation staticmethod ¤

equation(
    T: Union[float, FloatArray],
    A: float,
    B: float,
    C: float,
    D: float,
) -> Union[float, FloatArray]

DIPPR-102 equation.

Source code in src/polykin/properties/equations/dippr.py
253
254
255
256
257
258
259
260
261
@staticmethod
def equation(T: Union[float, FloatArray],
             A: float,
             B: float,
             C: float,
             D: float
             ) -> Union[float, FloatArray]:
    r"""DIPPR-102 equation."""
    return (A * T**B) / (1 + C/T + D/T**2)

fit ¤

fit(
    T: FloatVectorLike,
    Y: FloatVectorLike,
    sigmaY: Optional[FloatVectorLike] = None,
    fit_only: Optional[list[str]] = None,
    logY: bool = False,
    plot: bool = True,
) -> dict

Fit equation to data using non-linear regression.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: FloatVector

Y

Property to be fitted. Unit = Any.

TYPE: FloatVector

sigmaY

Standard deviation of Y. Unit = [Y].

TYPE: FloatVector | None DEFAULT: None

fit_only

List with name of parameters to be fitted.

TYPE: list[str] | None DEFAULT: None

logY

If True, the fit will be done in terms of log(Y).

TYPE: bool DEFAULT: False

plot

If True a plot comparing data and fitted correlation will be generated.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
dict

A dictionary of results with the following keys: 'success', 'parameters', 'covariance', and 'plot'.

Source code in src/polykin/properties/equations/base.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def fit(self,
        T: FloatVectorLike,
        Y: FloatVectorLike,
        sigmaY: Optional[FloatVectorLike] = None,
        fit_only: Optional[list[str]] = None,
        logY: bool = False,
        plot: bool = True,
        ) -> dict:
    """Fit equation to data using non-linear regression.

    Parameters
    ----------
    T : FloatVector
        Temperature. Unit = K.
    Y : FloatVector
        Property to be fitted. Unit = Any.
    sigmaY : FloatVector | None
        Standard deviation of Y. Unit = [Y].
    fit_only : list[str] | None
        List with name of parameters to be fitted.
    logY : bool
        If `True`, the fit will be done in terms of log(Y).
    plot : bool
        If `True` a plot comparing data and fitted correlation will be
        generated.

    Returns
    -------
    dict
        A dictionary of results with the following keys: 'success',
        'parameters', 'covariance', and 'plot'.
    """

    # Current parameter values
    pdict = self.p.copy()

    # Select parameters to be fitted
    pnames_fit = [name for name, info in self._pinfo.items() if info[1]]
    if fit_only:
        pnames_fit = set(fit_only) & set(pnames_fit)
    p0 = [pdict[pname] for pname in pnames_fit]

    # Fit function
    def ffit(x, *p):
        for pname, pvalue in zip(pnames_fit, p):
            pdict[pname] = pvalue
        Yfit = self.equation(T=x, **pdict)
        if logY:
            Yfit = log(Yfit)
        return Yfit

    solution = curve_fit(ffit,
                         xdata=T,
                         ydata=log(Y) if logY else Y,
                         p0=p0,
                         sigma=sigmaY,
                         absolute_sigma=False,
                         full_output=True)
    result = {}
    result['success'] = bool(solution[4])
    if solution[4]:
        popt = solution[0]
        pcov = solution[1]
        print("Fit successful.")
        for pname, pvalue in zip(pnames_fit, popt):
            print(f"{pname}: {pvalue}")
        print("Covariance:")
        print(pcov)
        result['covariance'] = pcov

        # Update attributes
        self.Trange = (min(T), max(T))
        for pname, pvalue in zip(pnames_fit, popt):
            self.p[pname] = pvalue
        result['parameters'] = pdict

        # plot
        if plot:
            kind = 'semilogy' if logY else 'linear'
            fig, ax = self.plot(kind=kind, return_objects=True)  # ok
            ax.plot(T, Y, 'o', mfc='none')
            result['plot'] = (fig, ax)
    else:
        print("Fit error: ", solution[3])
        result['message'] = solution[3]

    return result

plot ¤

plot(
    kind: Literal[
        "linear", "semilogy", "Arrhenius"
    ] = "linear",
    Trange: Optional[tuple[float, float]] = None,
    Tunit: Literal["C", "K"] = "K",
    title: Optional[str] = None,
    axes: Optional[Axes] = None,
    return_objects: bool = False,
) -> Optional[tuple[Optional[Figure], Axes]]

Plot quantity as a function of temperature.

PARAMETER DESCRIPTION
kind

Kind of plot to be generated.

TYPE: Literal['linear', 'semilogy', 'Arrhenius'] DEFAULT: 'linear'

Trange

Temperature range for x-axis. If None, the validity range (Tmin, Tmax) will be used. If no validity range was defined, the range will default to 0-100°C.

TYPE: tuple[float, float] | None DEFAULT: None

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

title

Title of plot. If None, the object name will be used.

TYPE: str | None DEFAULT: None

axes

Matplotlib Axes object.

TYPE: Axes | None DEFAULT: None

return_objects

If True, the Figure and Axes objects are returned (for saving or further manipulations).

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
tuple[Figure | None, Axes] | None

Figure and Axes objects if return_objects is True.

Source code in src/polykin/properties/equations/base.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def plot(self,
         kind: Literal['linear', 'semilogy', 'Arrhenius'] = 'linear',
         Trange: Optional[tuple[float, float]] = None,
         Tunit: Literal['C', 'K'] = 'K',
         title: Optional[str] = None,
         axes: Optional[Axes] = None,
         return_objects: bool = False
         ) -> Optional[tuple[Optional[Figure], Axes]]:
    """Plot quantity as a function of temperature.

    Parameters
    ----------
    kind : Literal['linear', 'semilogy', 'Arrhenius']
        Kind of plot to be generated.
    Trange : tuple[float, float] | None
        Temperature range for x-axis. If `None`, the validity range
        (Tmin, Tmax) will be used. If no validity range was defined, the
        range will default to 0-100°C.
    Tunit : Literal['C', 'K']
        Temperature unit.
    title : str | None
        Title of plot. If `None`, the object name will be used.
    axes : Axes | None
        Matplotlib Axes object.
    return_objects : bool
        If `True`, the Figure and Axes objects are returned (for saving or
        further manipulations).

    Returns
    -------
    tuple[Figure | None, Axes] | None
        Figure and Axes objects if return_objects is `True`.
    """

    # Check inputs
    check_in_set(kind, {'linear', 'semilogy', 'Arrhenius'}, 'kind')
    check_in_set(Tunit, {'K', 'C'}, 'Tunit')
    if Trange is not None:
        Trange_min = 0.
        if Tunit == 'C':
            Trange_min = -273.15
        check_valid_range(Trange, Trange_min, np.inf, 'Trange')

    # Plot objects
    if axes is None:
        fig, ax = plt.subplots()
        if title is None:
            title = self.name
        if title:
            fig.suptitle(title)
        label = None
    else:
        fig = None
        ax = axes
        label = self.name

    # Units and xlabel
    Tunit_range = Tunit
    if kind == 'Arrhenius':
        Tunit = 'K'
    Tsymbol = Tunit
    if Tunit == 'C':
        Tsymbol = '°' + Tunit

    if kind == 'Arrhenius':
        xlabel = r"$1/T$ [" + Tsymbol + r"$^{-1}$]"
    else:
        xlabel = fr"$T$ [{Tsymbol}]"

    # ylabel
    ylabel = fr"${self.symbol}$ [{self.unit}]"
    if axes is not None:
        ylabel0 = ax.get_ylabel()
        if ylabel0 and ylabel not in ylabel0:
            ylabel = ylabel0 + ", " + ylabel

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True)

    # x-axis vector
    if Trange is not None:
        if Tunit_range == 'C':
            Trange = (Trange[0]+273.15, Trange[1]+273.15)
    else:
        Trange = (np.min(self.Trange[0]), np.max(self.Trange[1]))
        if Trange == (0.0, np.inf):
            Trange = (273.15, 373.15)

    try:
        shape = self._shape
    except AttributeError:
        shape = None
    if shape is not None:
        print("Plot method not yet implemented for array-like equations.")
    else:
        TK = np.linspace(*Trange, 100)
        y = self.__call__(TK, 'K')
        T = TK
        if Tunit == 'C':
            T -= 273.15
        if kind == 'linear':
            ax.plot(T, y, label=label)
        elif kind == 'semilogy':
            ax.semilogy(T, y, label=label)
        elif kind == 'Arrhenius':
            ax.semilogy(1/TK, y, label=label)

    if fig is None:
        ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")

    if return_objects:
        return (fig, ax)

DIPPR104 ¤

DIPPR-104 equation.

This equation implements the following temperature dependence:

\[ Y = A + B/T + C/T^3 + D/T^8 + E/T^9 \]

where \(A\) to \(E\) are component-specific constants and \(T\) is the absolute temperature.

PARAMETER DESCRIPTION
A

Parameter of equation.

TYPE: float

B

Parameter of equation.

TYPE: float

C

Parameter of equation.

TYPE: float DEFAULT: 0.0

D

Parameter of equation.

TYPE: float DEFAULT: 0.0

E

Parameter of equation.

TYPE: float DEFAULT: 0.0

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

unit

Unit of output variable \(Y\).

TYPE: str DEFAULT: '-'

symbol

Symbol of output variable \(Y\).

TYPE: str DEFAULT: 'Y'

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/equations/dippr.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
class DIPPR104(DIPPRP5):
    r"""[DIPPR](https://de.wikipedia.org/wiki/DIPPR-Gleichungen)-104 equation.

    This equation implements the following temperature dependence:

    $$ Y = A + B/T + C/T^3 + D/T^8 + E/T^9 $$

    where $A$ to $E$ are component-specific constants and $T$ is the absolute
    temperature.

    Parameters
    ----------
    A : float
        Parameter of equation.
    B : float
        Parameter of equation.
    C : float
        Parameter of equation.
    D : float
        Parameter of equation.
    E : float
        Parameter of equation.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    unit : str
        Unit of output variable $Y$.
    symbol : str
        Symbol of output variable $Y$.
    name : str
        Name.
    """

    _pinfo = {'A': ('#', True), 'B': ('#·K', True), 'C': ('#·K³', True),
              'D': ('#·K⁸', True), 'E': ('#·K⁹', True)}

    def __init__(self,
                 A: float,
                 B: float,
                 C: float = 0.,
                 D: float = 0.,
                 E: float = 0.,
                 Tmin: float = 0.0,
                 Tmax: float = np.inf,
                 unit: str = '-',
                 symbol: str = 'Y',
                 name: str = ''
                 ) -> None:

        super().__init__(A, B, C, D, E, Tmin, Tmax, unit, symbol, name)

    @staticmethod
    def equation(T: Union[float, FloatArray],
                 A: float,
                 B: float,
                 C: float,
                 D: float,
                 E: float
                 ) -> Union[float, FloatArray]:
        r"""DIPPR-104 equation."""
        return A + B/T + C/T**3 + D/T**8 + E/T**9

__call__ ¤

__call__(
    T: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
) -> Union[float, FloatArray]

Evaluate property equation at given temperature, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

RETURNS DESCRIPTION
float | FloatArray

Correlation value.

Source code in src/polykin/properties/equations/base.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __call__(self,
             T: Union[float, FloatArrayLike],
             Tunit: Literal['C', 'K'] = 'K'
             ) -> Union[float, FloatArray]:
    r"""Evaluate property equation at given temperature, including unit
    conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    Tunit : Literal['C', 'K']
        Temperature unit.

    Returns
    -------
    float | FloatArray
        Correlation value.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    return self.equation(TK, **self.p)

equation staticmethod ¤

equation(
    T: Union[float, FloatArray],
    A: float,
    B: float,
    C: float,
    D: float,
    E: float,
) -> Union[float, FloatArray]

DIPPR-104 equation.

Source code in src/polykin/properties/equations/dippr.py
318
319
320
321
322
323
324
325
326
327
@staticmethod
def equation(T: Union[float, FloatArray],
             A: float,
             B: float,
             C: float,
             D: float,
             E: float
             ) -> Union[float, FloatArray]:
    r"""DIPPR-104 equation."""
    return A + B/T + C/T**3 + D/T**8 + E/T**9

fit ¤

fit(
    T: FloatVectorLike,
    Y: FloatVectorLike,
    sigmaY: Optional[FloatVectorLike] = None,
    fit_only: Optional[list[str]] = None,
    logY: bool = False,
    plot: bool = True,
) -> dict

Fit equation to data using non-linear regression.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: FloatVector

Y

Property to be fitted. Unit = Any.

TYPE: FloatVector

sigmaY

Standard deviation of Y. Unit = [Y].

TYPE: FloatVector | None DEFAULT: None

fit_only

List with name of parameters to be fitted.

TYPE: list[str] | None DEFAULT: None

logY

If True, the fit will be done in terms of log(Y).

TYPE: bool DEFAULT: False

plot

If True a plot comparing data and fitted correlation will be generated.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
dict

A dictionary of results with the following keys: 'success', 'parameters', 'covariance', and 'plot'.

Source code in src/polykin/properties/equations/base.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def fit(self,
        T: FloatVectorLike,
        Y: FloatVectorLike,
        sigmaY: Optional[FloatVectorLike] = None,
        fit_only: Optional[list[str]] = None,
        logY: bool = False,
        plot: bool = True,
        ) -> dict:
    """Fit equation to data using non-linear regression.

    Parameters
    ----------
    T : FloatVector
        Temperature. Unit = K.
    Y : FloatVector
        Property to be fitted. Unit = Any.
    sigmaY : FloatVector | None
        Standard deviation of Y. Unit = [Y].
    fit_only : list[str] | None
        List with name of parameters to be fitted.
    logY : bool
        If `True`, the fit will be done in terms of log(Y).
    plot : bool
        If `True` a plot comparing data and fitted correlation will be
        generated.

    Returns
    -------
    dict
        A dictionary of results with the following keys: 'success',
        'parameters', 'covariance', and 'plot'.
    """

    # Current parameter values
    pdict = self.p.copy()

    # Select parameters to be fitted
    pnames_fit = [name for name, info in self._pinfo.items() if info[1]]
    if fit_only:
        pnames_fit = set(fit_only) & set(pnames_fit)
    p0 = [pdict[pname] for pname in pnames_fit]

    # Fit function
    def ffit(x, *p):
        for pname, pvalue in zip(pnames_fit, p):
            pdict[pname] = pvalue
        Yfit = self.equation(T=x, **pdict)
        if logY:
            Yfit = log(Yfit)
        return Yfit

    solution = curve_fit(ffit,
                         xdata=T,
                         ydata=log(Y) if logY else Y,
                         p0=p0,
                         sigma=sigmaY,
                         absolute_sigma=False,
                         full_output=True)
    result = {}
    result['success'] = bool(solution[4])
    if solution[4]:
        popt = solution[0]
        pcov = solution[1]
        print("Fit successful.")
        for pname, pvalue in zip(pnames_fit, popt):
            print(f"{pname}: {pvalue}")
        print("Covariance:")
        print(pcov)
        result['covariance'] = pcov

        # Update attributes
        self.Trange = (min(T), max(T))
        for pname, pvalue in zip(pnames_fit, popt):
            self.p[pname] = pvalue
        result['parameters'] = pdict

        # plot
        if plot:
            kind = 'semilogy' if logY else 'linear'
            fig, ax = self.plot(kind=kind, return_objects=True)  # ok
            ax.plot(T, Y, 'o', mfc='none')
            result['plot'] = (fig, ax)
    else:
        print("Fit error: ", solution[3])
        result['message'] = solution[3]

    return result

plot ¤

plot(
    kind: Literal[
        "linear", "semilogy", "Arrhenius"
    ] = "linear",
    Trange: Optional[tuple[float, float]] = None,
    Tunit: Literal["C", "K"] = "K",
    title: Optional[str] = None,
    axes: Optional[Axes] = None,
    return_objects: bool = False,
) -> Optional[tuple[Optional[Figure], Axes]]

Plot quantity as a function of temperature.

PARAMETER DESCRIPTION
kind

Kind of plot to be generated.

TYPE: Literal['linear', 'semilogy', 'Arrhenius'] DEFAULT: 'linear'

Trange

Temperature range for x-axis. If None, the validity range (Tmin, Tmax) will be used. If no validity range was defined, the range will default to 0-100°C.

TYPE: tuple[float, float] | None DEFAULT: None

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

title

Title of plot. If None, the object name will be used.

TYPE: str | None DEFAULT: None

axes

Matplotlib Axes object.

TYPE: Axes | None DEFAULT: None

return_objects

If True, the Figure and Axes objects are returned (for saving or further manipulations).

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
tuple[Figure | None, Axes] | None

Figure and Axes objects if return_objects is True.

Source code in src/polykin/properties/equations/base.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def plot(self,
         kind: Literal['linear', 'semilogy', 'Arrhenius'] = 'linear',
         Trange: Optional[tuple[float, float]] = None,
         Tunit: Literal['C', 'K'] = 'K',
         title: Optional[str] = None,
         axes: Optional[Axes] = None,
         return_objects: bool = False
         ) -> Optional[tuple[Optional[Figure], Axes]]:
    """Plot quantity as a function of temperature.

    Parameters
    ----------
    kind : Literal['linear', 'semilogy', 'Arrhenius']
        Kind of plot to be generated.
    Trange : tuple[float, float] | None
        Temperature range for x-axis. If `None`, the validity range
        (Tmin, Tmax) will be used. If no validity range was defined, the
        range will default to 0-100°C.
    Tunit : Literal['C', 'K']
        Temperature unit.
    title : str | None
        Title of plot. If `None`, the object name will be used.
    axes : Axes | None
        Matplotlib Axes object.
    return_objects : bool
        If `True`, the Figure and Axes objects are returned (for saving or
        further manipulations).

    Returns
    -------
    tuple[Figure | None, Axes] | None
        Figure and Axes objects if return_objects is `True`.
    """

    # Check inputs
    check_in_set(kind, {'linear', 'semilogy', 'Arrhenius'}, 'kind')
    check_in_set(Tunit, {'K', 'C'}, 'Tunit')
    if Trange is not None:
        Trange_min = 0.
        if Tunit == 'C':
            Trange_min = -273.15
        check_valid_range(Trange, Trange_min, np.inf, 'Trange')

    # Plot objects
    if axes is None:
        fig, ax = plt.subplots()
        if title is None:
            title = self.name
        if title:
            fig.suptitle(title)
        label = None
    else:
        fig = None
        ax = axes
        label = self.name

    # Units and xlabel
    Tunit_range = Tunit
    if kind == 'Arrhenius':
        Tunit = 'K'
    Tsymbol = Tunit
    if Tunit == 'C':
        Tsymbol = '°' + Tunit

    if kind == 'Arrhenius':
        xlabel = r"$1/T$ [" + Tsymbol + r"$^{-1}$]"
    else:
        xlabel = fr"$T$ [{Tsymbol}]"

    # ylabel
    ylabel = fr"${self.symbol}$ [{self.unit}]"
    if axes is not None:
        ylabel0 = ax.get_ylabel()
        if ylabel0 and ylabel not in ylabel0:
            ylabel = ylabel0 + ", " + ylabel

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True)

    # x-axis vector
    if Trange is not None:
        if Tunit_range == 'C':
            Trange = (Trange[0]+273.15, Trange[1]+273.15)
    else:
        Trange = (np.min(self.Trange[0]), np.max(self.Trange[1]))
        if Trange == (0.0, np.inf):
            Trange = (273.15, 373.15)

    try:
        shape = self._shape
    except AttributeError:
        shape = None
    if shape is not None:
        print("Plot method not yet implemented for array-like equations.")
    else:
        TK = np.linspace(*Trange, 100)
        y = self.__call__(TK, 'K')
        T = TK
        if Tunit == 'C':
            T -= 273.15
        if kind == 'linear':
            ax.plot(T, y, label=label)
        elif kind == 'semilogy':
            ax.semilogy(T, y, label=label)
        elif kind == 'Arrhenius':
            ax.semilogy(1/TK, y, label=label)

    if fig is None:
        ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")

    if return_objects:
        return (fig, ax)

DIPPR105 ¤

DIPPR-105 equation.

This equation implements the following temperature dependence:

\[ Y = \frac{A}{B^{ \left( 1 + (1 - T / C)^D \right) }} \]

where \(A\) to \(D\) are component-specific constants and \(T\) is the absolute temperature.

PARAMETER DESCRIPTION
A

Parameter of equation.

TYPE: float

B

Parameter of equation.

TYPE: float

C

Parameter of equation.

TYPE: float

D

Parameter of equation.

TYPE: float

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

unit

Unit of output variable \(Y\).

TYPE: str DEFAULT: '-'

symbol

Symbol of output variable \(Y\).

TYPE: str DEFAULT: 'Y'

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/equations/dippr.py
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
class DIPPR105(DIPPRP4):
    r"""[DIPPR](https://de.wikipedia.org/wiki/DIPPR-Gleichungen)-105 equation.

    This equation implements the following temperature dependence:

    $$ Y = \frac{A}{B^{ \left( 1 + (1 - T / C)^D \right) }} $$

    where $A$ to $D$ are component-specific constants and $T$ is the absolute
    temperature.

    Parameters
    ----------
    A : float
        Parameter of equation.
    B : float
        Parameter of equation.
    C : float
        Parameter of equation.
    D : float
        Parameter of equation.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    unit : str
        Unit of output variable $Y$.
    symbol : str
        Symbol of output variable $Y$.
    name : str
        Name.
    """

    _pinfo = {'A': ('#', True), 'B': ('', True), 'C': ('K', True),
              'D': ('', True)}

    def __init__(self,
                 A: float,
                 B: float,
                 C: float,
                 D: float,
                 Tmin: float = 0.0,
                 Tmax: float = np.inf,
                 unit: str = '-',
                 symbol: str = 'Y',
                 name: str = ''
                 ) -> None:

        super().__init__(A, B, C, D, Tmin, Tmax, unit, symbol, name)

    @staticmethod
    def equation(T: Union[float, FloatArray],
                 A: float,
                 B: float,
                 C: float,
                 D: float
                 ) -> Union[float, FloatArray]:
        r"""DIPPR-105 equation."""
        return A / B**(1 + (1 - T / C)**D)

__call__ ¤

__call__(
    T: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
) -> Union[float, FloatArray]

Evaluate property equation at given temperature, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

RETURNS DESCRIPTION
float | FloatArray

Correlation value.

Source code in src/polykin/properties/equations/base.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __call__(self,
             T: Union[float, FloatArrayLike],
             Tunit: Literal['C', 'K'] = 'K'
             ) -> Union[float, FloatArray]:
    r"""Evaluate property equation at given temperature, including unit
    conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    Tunit : Literal['C', 'K']
        Temperature unit.

    Returns
    -------
    float | FloatArray
        Correlation value.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    return self.equation(TK, **self.p)

equation staticmethod ¤

equation(
    T: Union[float, FloatArray],
    A: float,
    B: float,
    C: float,
    D: float,
) -> Union[float, FloatArray]

DIPPR-105 equation.

Source code in src/polykin/properties/equations/dippr.py
381
382
383
384
385
386
387
388
389
@staticmethod
def equation(T: Union[float, FloatArray],
             A: float,
             B: float,
             C: float,
             D: float
             ) -> Union[float, FloatArray]:
    r"""DIPPR-105 equation."""
    return A / B**(1 + (1 - T / C)**D)

fit ¤

fit(
    T: FloatVectorLike,
    Y: FloatVectorLike,
    sigmaY: Optional[FloatVectorLike] = None,
    fit_only: Optional[list[str]] = None,
    logY: bool = False,
    plot: bool = True,
) -> dict

Fit equation to data using non-linear regression.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: FloatVector

Y

Property to be fitted. Unit = Any.

TYPE: FloatVector

sigmaY

Standard deviation of Y. Unit = [Y].

TYPE: FloatVector | None DEFAULT: None

fit_only

List with name of parameters to be fitted.

TYPE: list[str] | None DEFAULT: None

logY

If True, the fit will be done in terms of log(Y).

TYPE: bool DEFAULT: False

plot

If True a plot comparing data and fitted correlation will be generated.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
dict

A dictionary of results with the following keys: 'success', 'parameters', 'covariance', and 'plot'.

Source code in src/polykin/properties/equations/base.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def fit(self,
        T: FloatVectorLike,
        Y: FloatVectorLike,
        sigmaY: Optional[FloatVectorLike] = None,
        fit_only: Optional[list[str]] = None,
        logY: bool = False,
        plot: bool = True,
        ) -> dict:
    """Fit equation to data using non-linear regression.

    Parameters
    ----------
    T : FloatVector
        Temperature. Unit = K.
    Y : FloatVector
        Property to be fitted. Unit = Any.
    sigmaY : FloatVector | None
        Standard deviation of Y. Unit = [Y].
    fit_only : list[str] | None
        List with name of parameters to be fitted.
    logY : bool
        If `True`, the fit will be done in terms of log(Y).
    plot : bool
        If `True` a plot comparing data and fitted correlation will be
        generated.

    Returns
    -------
    dict
        A dictionary of results with the following keys: 'success',
        'parameters', 'covariance', and 'plot'.
    """

    # Current parameter values
    pdict = self.p.copy()

    # Select parameters to be fitted
    pnames_fit = [name for name, info in self._pinfo.items() if info[1]]
    if fit_only:
        pnames_fit = set(fit_only) & set(pnames_fit)
    p0 = [pdict[pname] for pname in pnames_fit]

    # Fit function
    def ffit(x, *p):
        for pname, pvalue in zip(pnames_fit, p):
            pdict[pname] = pvalue
        Yfit = self.equation(T=x, **pdict)
        if logY:
            Yfit = log(Yfit)
        return Yfit

    solution = curve_fit(ffit,
                         xdata=T,
                         ydata=log(Y) if logY else Y,
                         p0=p0,
                         sigma=sigmaY,
                         absolute_sigma=False,
                         full_output=True)
    result = {}
    result['success'] = bool(solution[4])
    if solution[4]:
        popt = solution[0]
        pcov = solution[1]
        print("Fit successful.")
        for pname, pvalue in zip(pnames_fit, popt):
            print(f"{pname}: {pvalue}")
        print("Covariance:")
        print(pcov)
        result['covariance'] = pcov

        # Update attributes
        self.Trange = (min(T), max(T))
        for pname, pvalue in zip(pnames_fit, popt):
            self.p[pname] = pvalue
        result['parameters'] = pdict

        # plot
        if plot:
            kind = 'semilogy' if logY else 'linear'
            fig, ax = self.plot(kind=kind, return_objects=True)  # ok
            ax.plot(T, Y, 'o', mfc='none')
            result['plot'] = (fig, ax)
    else:
        print("Fit error: ", solution[3])
        result['message'] = solution[3]

    return result

plot ¤

plot(
    kind: Literal[
        "linear", "semilogy", "Arrhenius"
    ] = "linear",
    Trange: Optional[tuple[float, float]] = None,
    Tunit: Literal["C", "K"] = "K",
    title: Optional[str] = None,
    axes: Optional[Axes] = None,
    return_objects: bool = False,
) -> Optional[tuple[Optional[Figure], Axes]]

Plot quantity as a function of temperature.

PARAMETER DESCRIPTION
kind

Kind of plot to be generated.

TYPE: Literal['linear', 'semilogy', 'Arrhenius'] DEFAULT: 'linear'

Trange

Temperature range for x-axis. If None, the validity range (Tmin, Tmax) will be used. If no validity range was defined, the range will default to 0-100°C.

TYPE: tuple[float, float] | None DEFAULT: None

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

title

Title of plot. If None, the object name will be used.

TYPE: str | None DEFAULT: None

axes

Matplotlib Axes object.

TYPE: Axes | None DEFAULT: None

return_objects

If True, the Figure and Axes objects are returned (for saving or further manipulations).

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
tuple[Figure | None, Axes] | None

Figure and Axes objects if return_objects is True.

Source code in src/polykin/properties/equations/base.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def plot(self,
         kind: Literal['linear', 'semilogy', 'Arrhenius'] = 'linear',
         Trange: Optional[tuple[float, float]] = None,
         Tunit: Literal['C', 'K'] = 'K',
         title: Optional[str] = None,
         axes: Optional[Axes] = None,
         return_objects: bool = False
         ) -> Optional[tuple[Optional[Figure], Axes]]:
    """Plot quantity as a function of temperature.

    Parameters
    ----------
    kind : Literal['linear', 'semilogy', 'Arrhenius']
        Kind of plot to be generated.
    Trange : tuple[float, float] | None
        Temperature range for x-axis. If `None`, the validity range
        (Tmin, Tmax) will be used. If no validity range was defined, the
        range will default to 0-100°C.
    Tunit : Literal['C', 'K']
        Temperature unit.
    title : str | None
        Title of plot. If `None`, the object name will be used.
    axes : Axes | None
        Matplotlib Axes object.
    return_objects : bool
        If `True`, the Figure and Axes objects are returned (for saving or
        further manipulations).

    Returns
    -------
    tuple[Figure | None, Axes] | None
        Figure and Axes objects if return_objects is `True`.
    """

    # Check inputs
    check_in_set(kind, {'linear', 'semilogy', 'Arrhenius'}, 'kind')
    check_in_set(Tunit, {'K', 'C'}, 'Tunit')
    if Trange is not None:
        Trange_min = 0.
        if Tunit == 'C':
            Trange_min = -273.15
        check_valid_range(Trange, Trange_min, np.inf, 'Trange')

    # Plot objects
    if axes is None:
        fig, ax = plt.subplots()
        if title is None:
            title = self.name
        if title:
            fig.suptitle(title)
        label = None
    else:
        fig = None
        ax = axes
        label = self.name

    # Units and xlabel
    Tunit_range = Tunit
    if kind == 'Arrhenius':
        Tunit = 'K'
    Tsymbol = Tunit
    if Tunit == 'C':
        Tsymbol = '°' + Tunit

    if kind == 'Arrhenius':
        xlabel = r"$1/T$ [" + Tsymbol + r"$^{-1}$]"
    else:
        xlabel = fr"$T$ [{Tsymbol}]"

    # ylabel
    ylabel = fr"${self.symbol}$ [{self.unit}]"
    if axes is not None:
        ylabel0 = ax.get_ylabel()
        if ylabel0 and ylabel not in ylabel0:
            ylabel = ylabel0 + ", " + ylabel

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True)

    # x-axis vector
    if Trange is not None:
        if Tunit_range == 'C':
            Trange = (Trange[0]+273.15, Trange[1]+273.15)
    else:
        Trange = (np.min(self.Trange[0]), np.max(self.Trange[1]))
        if Trange == (0.0, np.inf):
            Trange = (273.15, 373.15)

    try:
        shape = self._shape
    except AttributeError:
        shape = None
    if shape is not None:
        print("Plot method not yet implemented for array-like equations.")
    else:
        TK = np.linspace(*Trange, 100)
        y = self.__call__(TK, 'K')
        T = TK
        if Tunit == 'C':
            T -= 273.15
        if kind == 'linear':
            ax.plot(T, y, label=label)
        elif kind == 'semilogy':
            ax.semilogy(T, y, label=label)
        elif kind == 'Arrhenius':
            ax.semilogy(1/TK, y, label=label)

    if fig is None:
        ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")

    if return_objects:
        return (fig, ax)

DIPPR106 ¤

DIPPR-106 equation.

This equation implements the following temperature dependence:

\[ Y = A (1 - T_r)^{B + C T_r + D T_r^2 + E T_r^3} \]

where \(A\) to \(E\) are component-specific constants, \(T\) is the absolute temperature, \(T_c\) is the critical temperature and \(T_r = T/T_c\) is the reduced temperature.

PARAMETER DESCRIPTION
Tc

Critical temperature. Unit = K.

TYPE: float

A

Parameter of equation.

TYPE: float

B

Parameter of equation.

TYPE: float

C

Parameter of equation.

TYPE: float DEFAULT: 0.0

D

Parameter of equation.

TYPE: float DEFAULT: 0.0

E

Parameter of equation.

TYPE: float DEFAULT: 0.0

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

unit

Unit of output variable \(Y\).

TYPE: str DEFAULT: '-'

symbol

Symbol of output variable \(Y\).

TYPE: str DEFAULT: 'Y'

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/equations/dippr.py
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
class DIPPR106(DIPPR):
    r"""[DIPPR](https://de.wikipedia.org/wiki/DIPPR-Gleichungen)-106 equation.

    This equation implements the following temperature dependence:

    $$ Y = A (1 - T_r)^{B + C T_r + D T_r^2 + E T_r^3} $$

    where $A$ to $E$ are component-specific constants, $T$ is the absolute
    temperature, $T_c$ is the critical temperature and $T_r = T/T_c$ is the
    reduced temperature.

    Parameters
    ----------
    Tc : float
        Critical temperature.
        Unit = K.
    A : float
        Parameter of equation.
    B : float
        Parameter of equation.
    C : float
        Parameter of equation.
    D : float
        Parameter of equation.
    E : float
        Parameter of equation.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    unit : str
        Unit of output variable $Y$.
    symbol : str
        Symbol of output variable $Y$.
    name : str
        Name.
    """

    _pinfo = {'A': ('#', True), 'B': ('', True), 'C': ('', True),
              'D': ('', True), 'E': ('', True), 'Tc': ('K', False)}

    def __init__(self,
                 Tc: float,
                 A: float,
                 B: float,
                 C: float = 0.,
                 D: float = 0.,
                 E: float = 0.,
                 Tmin: float = 0.0,
                 Tmax: float = np.inf,
                 unit: str = '-',
                 symbol: str = 'Y',
                 name: str = ''
                 ) -> None:

        self.p = {'A': A, 'B': B, 'C': C, 'D': D, 'E': E, 'Tc': Tc}
        super().__init__((Tmin, Tmax), unit, symbol, name)

    @staticmethod
    def equation(T: Union[float, FloatArray],
                 A: float,
                 B: float,
                 C: float,
                 D: float,
                 E: float,
                 Tc: float,
                 ) -> Union[float, FloatArray]:
        r"""DIPPR-106 equation."""
        Tr = T/Tc
        return A*(1-Tr)**(B + Tr*(C + Tr*(D + E*Tr)))

__call__ ¤

__call__(
    T: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
) -> Union[float, FloatArray]

Evaluate property equation at given temperature, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

RETURNS DESCRIPTION
float | FloatArray

Correlation value.

Source code in src/polykin/properties/equations/base.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __call__(self,
             T: Union[float, FloatArrayLike],
             Tunit: Literal['C', 'K'] = 'K'
             ) -> Union[float, FloatArray]:
    r"""Evaluate property equation at given temperature, including unit
    conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    Tunit : Literal['C', 'K']
        Temperature unit.

    Returns
    -------
    float | FloatArray
        Correlation value.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    return self.equation(TK, **self.p)

equation staticmethod ¤

equation(
    T: Union[float, FloatArray],
    A: float,
    B: float,
    C: float,
    D: float,
    E: float,
    Tc: float,
) -> Union[float, FloatArray]

DIPPR-106 equation.

Source code in src/polykin/properties/equations/dippr.py
452
453
454
455
456
457
458
459
460
461
462
463
@staticmethod
def equation(T: Union[float, FloatArray],
             A: float,
             B: float,
             C: float,
             D: float,
             E: float,
             Tc: float,
             ) -> Union[float, FloatArray]:
    r"""DIPPR-106 equation."""
    Tr = T/Tc
    return A*(1-Tr)**(B + Tr*(C + Tr*(D + E*Tr)))

fit ¤

fit(
    T: FloatVectorLike,
    Y: FloatVectorLike,
    sigmaY: Optional[FloatVectorLike] = None,
    fit_only: Optional[list[str]] = None,
    logY: bool = False,
    plot: bool = True,
) -> dict

Fit equation to data using non-linear regression.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: FloatVector

Y

Property to be fitted. Unit = Any.

TYPE: FloatVector

sigmaY

Standard deviation of Y. Unit = [Y].

TYPE: FloatVector | None DEFAULT: None

fit_only

List with name of parameters to be fitted.

TYPE: list[str] | None DEFAULT: None

logY

If True, the fit will be done in terms of log(Y).

TYPE: bool DEFAULT: False

plot

If True a plot comparing data and fitted correlation will be generated.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
dict

A dictionary of results with the following keys: 'success', 'parameters', 'covariance', and 'plot'.

Source code in src/polykin/properties/equations/base.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def fit(self,
        T: FloatVectorLike,
        Y: FloatVectorLike,
        sigmaY: Optional[FloatVectorLike] = None,
        fit_only: Optional[list[str]] = None,
        logY: bool = False,
        plot: bool = True,
        ) -> dict:
    """Fit equation to data using non-linear regression.

    Parameters
    ----------
    T : FloatVector
        Temperature. Unit = K.
    Y : FloatVector
        Property to be fitted. Unit = Any.
    sigmaY : FloatVector | None
        Standard deviation of Y. Unit = [Y].
    fit_only : list[str] | None
        List with name of parameters to be fitted.
    logY : bool
        If `True`, the fit will be done in terms of log(Y).
    plot : bool
        If `True` a plot comparing data and fitted correlation will be
        generated.

    Returns
    -------
    dict
        A dictionary of results with the following keys: 'success',
        'parameters', 'covariance', and 'plot'.
    """

    # Current parameter values
    pdict = self.p.copy()

    # Select parameters to be fitted
    pnames_fit = [name for name, info in self._pinfo.items() if info[1]]
    if fit_only:
        pnames_fit = set(fit_only) & set(pnames_fit)
    p0 = [pdict[pname] for pname in pnames_fit]

    # Fit function
    def ffit(x, *p):
        for pname, pvalue in zip(pnames_fit, p):
            pdict[pname] = pvalue
        Yfit = self.equation(T=x, **pdict)
        if logY:
            Yfit = log(Yfit)
        return Yfit

    solution = curve_fit(ffit,
                         xdata=T,
                         ydata=log(Y) if logY else Y,
                         p0=p0,
                         sigma=sigmaY,
                         absolute_sigma=False,
                         full_output=True)
    result = {}
    result['success'] = bool(solution[4])
    if solution[4]:
        popt = solution[0]
        pcov = solution[1]
        print("Fit successful.")
        for pname, pvalue in zip(pnames_fit, popt):
            print(f"{pname}: {pvalue}")
        print("Covariance:")
        print(pcov)
        result['covariance'] = pcov

        # Update attributes
        self.Trange = (min(T), max(T))
        for pname, pvalue in zip(pnames_fit, popt):
            self.p[pname] = pvalue
        result['parameters'] = pdict

        # plot
        if plot:
            kind = 'semilogy' if logY else 'linear'
            fig, ax = self.plot(kind=kind, return_objects=True)  # ok
            ax.plot(T, Y, 'o', mfc='none')
            result['plot'] = (fig, ax)
    else:
        print("Fit error: ", solution[3])
        result['message'] = solution[3]

    return result

plot ¤

plot(
    kind: Literal[
        "linear", "semilogy", "Arrhenius"
    ] = "linear",
    Trange: Optional[tuple[float, float]] = None,
    Tunit: Literal["C", "K"] = "K",
    title: Optional[str] = None,
    axes: Optional[Axes] = None,
    return_objects: bool = False,
) -> Optional[tuple[Optional[Figure], Axes]]

Plot quantity as a function of temperature.

PARAMETER DESCRIPTION
kind

Kind of plot to be generated.

TYPE: Literal['linear', 'semilogy', 'Arrhenius'] DEFAULT: 'linear'

Trange

Temperature range for x-axis. If None, the validity range (Tmin, Tmax) will be used. If no validity range was defined, the range will default to 0-100°C.

TYPE: tuple[float, float] | None DEFAULT: None

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

title

Title of plot. If None, the object name will be used.

TYPE: str | None DEFAULT: None

axes

Matplotlib Axes object.

TYPE: Axes | None DEFAULT: None

return_objects

If True, the Figure and Axes objects are returned (for saving or further manipulations).

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
tuple[Figure | None, Axes] | None

Figure and Axes objects if return_objects is True.

Source code in src/polykin/properties/equations/base.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def plot(self,
         kind: Literal['linear', 'semilogy', 'Arrhenius'] = 'linear',
         Trange: Optional[tuple[float, float]] = None,
         Tunit: Literal['C', 'K'] = 'K',
         title: Optional[str] = None,
         axes: Optional[Axes] = None,
         return_objects: bool = False
         ) -> Optional[tuple[Optional[Figure], Axes]]:
    """Plot quantity as a function of temperature.

    Parameters
    ----------
    kind : Literal['linear', 'semilogy', 'Arrhenius']
        Kind of plot to be generated.
    Trange : tuple[float, float] | None
        Temperature range for x-axis. If `None`, the validity range
        (Tmin, Tmax) will be used. If no validity range was defined, the
        range will default to 0-100°C.
    Tunit : Literal['C', 'K']
        Temperature unit.
    title : str | None
        Title of plot. If `None`, the object name will be used.
    axes : Axes | None
        Matplotlib Axes object.
    return_objects : bool
        If `True`, the Figure and Axes objects are returned (for saving or
        further manipulations).

    Returns
    -------
    tuple[Figure | None, Axes] | None
        Figure and Axes objects if return_objects is `True`.
    """

    # Check inputs
    check_in_set(kind, {'linear', 'semilogy', 'Arrhenius'}, 'kind')
    check_in_set(Tunit, {'K', 'C'}, 'Tunit')
    if Trange is not None:
        Trange_min = 0.
        if Tunit == 'C':
            Trange_min = -273.15
        check_valid_range(Trange, Trange_min, np.inf, 'Trange')

    # Plot objects
    if axes is None:
        fig, ax = plt.subplots()
        if title is None:
            title = self.name
        if title:
            fig.suptitle(title)
        label = None
    else:
        fig = None
        ax = axes
        label = self.name

    # Units and xlabel
    Tunit_range = Tunit
    if kind == 'Arrhenius':
        Tunit = 'K'
    Tsymbol = Tunit
    if Tunit == 'C':
        Tsymbol = '°' + Tunit

    if kind == 'Arrhenius':
        xlabel = r"$1/T$ [" + Tsymbol + r"$^{-1}$]"
    else:
        xlabel = fr"$T$ [{Tsymbol}]"

    # ylabel
    ylabel = fr"${self.symbol}$ [{self.unit}]"
    if axes is not None:
        ylabel0 = ax.get_ylabel()
        if ylabel0 and ylabel not in ylabel0:
            ylabel = ylabel0 + ", " + ylabel

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True)

    # x-axis vector
    if Trange is not None:
        if Tunit_range == 'C':
            Trange = (Trange[0]+273.15, Trange[1]+273.15)
    else:
        Trange = (np.min(self.Trange[0]), np.max(self.Trange[1]))
        if Trange == (0.0, np.inf):
            Trange = (273.15, 373.15)

    try:
        shape = self._shape
    except AttributeError:
        shape = None
    if shape is not None:
        print("Plot method not yet implemented for array-like equations.")
    else:
        TK = np.linspace(*Trange, 100)
        y = self.__call__(TK, 'K')
        T = TK
        if Tunit == 'C':
            T -= 273.15
        if kind == 'linear':
            ax.plot(T, y, label=label)
        elif kind == 'semilogy':
            ax.semilogy(T, y, label=label)
        elif kind == 'Arrhenius':
            ax.semilogy(1/TK, y, label=label)

    if fig is None:
        ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")

    if return_objects:
        return (fig, ax)

DIPPR107 ¤

DIPPR-107 equation.

This equation implements the following temperature dependence:

\[ Y = A + B\left[{\frac {C/T}{\sinh \left(C/T\right)}}\right]^2 + D\left[{\frac {E/T}{\cosh \left(E/T\right)}}\right]^2 \]

where \(A\) to \(E\) are component-specific constants and \(T\) is the absolute temperature.

PARAMETER DESCRIPTION
A

Parameter of equation.

TYPE: float

B

Parameter of equation.

TYPE: float

C

Parameter of equation.

TYPE: float

D

Parameter of equation.

TYPE: float

E

Parameter of equation.

TYPE: float

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

unit

Unit of output variable \(Y\).

TYPE: str DEFAULT: '-'

symbol

Symbol of output variable \(Y\).

TYPE: str DEFAULT: 'Y'

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/equations/dippr.py
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
class DIPPR107(DIPPRP5):
    r"""[DIPPR](https://de.wikipedia.org/wiki/DIPPR-Gleichungen)-107 equation.

    This equation implements the following temperature dependence:

    $$ Y = A + B\left[{\frac {C/T}{\sinh \left(C/T\right)}}\right]^2 +
        D\left[{\frac {E/T}{\cosh \left(E/T\right)}}\right]^2 $$

    where $A$ to $E$ are component-specific constants and $T$ is the absolute
    temperature.

    Parameters
    ----------
    A : float
        Parameter of equation.
    B : float
        Parameter of equation.
    C : float
        Parameter of equation.
    D : float
        Parameter of equation.
    E : float
        Parameter of equation.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    unit : str
        Unit of output variable $Y$.
    symbol : str
        Symbol of output variable $Y$.
    name : str
        Name.
    """

    _pinfo = {'A': ('#', True), 'B': ('#', True), 'C': ('K', True),
              'D': ('#', True), 'E': ('K', True)}

    def __init__(self,
                 A: float,
                 B: float,
                 C: float,
                 D: float,
                 E: float,
                 Tmin: float = 0.0,
                 Tmax: float = np.inf,
                 unit: str = '-',
                 symbol: str = 'Y',
                 name: str = ''
                 ) -> None:

        super().__init__(A, B, C, D, E, Tmin, Tmax, unit, symbol, name)

    @staticmethod
    def equation(T: Union[float, FloatArray],
                 A: float,
                 B: float,
                 C: float,
                 D: float,
                 E: float
                 ) -> Union[float, FloatArray]:
        r"""DIPPR-107 equation."""
        return A + B*(C/T/sinh(C/T))**2 + D*(E/T/cosh(E/T))**2

__call__ ¤

__call__(
    T: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
) -> Union[float, FloatArray]

Evaluate property equation at given temperature, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

RETURNS DESCRIPTION
float | FloatArray

Correlation value.

Source code in src/polykin/properties/equations/base.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __call__(self,
             T: Union[float, FloatArrayLike],
             Tunit: Literal['C', 'K'] = 'K'
             ) -> Union[float, FloatArray]:
    r"""Evaluate property equation at given temperature, including unit
    conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    Tunit : Literal['C', 'K']
        Temperature unit.

    Returns
    -------
    float | FloatArray
        Correlation value.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    return self.equation(TK, **self.p)

equation staticmethod ¤

equation(
    T: Union[float, FloatArray],
    A: float,
    B: float,
    C: float,
    D: float,
    E: float,
) -> Union[float, FloatArray]

DIPPR-107 equation.

Source code in src/polykin/properties/equations/dippr.py
521
522
523
524
525
526
527
528
529
530
@staticmethod
def equation(T: Union[float, FloatArray],
             A: float,
             B: float,
             C: float,
             D: float,
             E: float
             ) -> Union[float, FloatArray]:
    r"""DIPPR-107 equation."""
    return A + B*(C/T/sinh(C/T))**2 + D*(E/T/cosh(E/T))**2

fit ¤

fit(
    T: FloatVectorLike,
    Y: FloatVectorLike,
    sigmaY: Optional[FloatVectorLike] = None,
    fit_only: Optional[list[str]] = None,
    logY: bool = False,
    plot: bool = True,
) -> dict

Fit equation to data using non-linear regression.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: FloatVector

Y

Property to be fitted. Unit = Any.

TYPE: FloatVector

sigmaY

Standard deviation of Y. Unit = [Y].

TYPE: FloatVector | None DEFAULT: None

fit_only

List with name of parameters to be fitted.

TYPE: list[str] | None DEFAULT: None

logY

If True, the fit will be done in terms of log(Y).

TYPE: bool DEFAULT: False

plot

If True a plot comparing data and fitted correlation will be generated.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
dict

A dictionary of results with the following keys: 'success', 'parameters', 'covariance', and 'plot'.

Source code in src/polykin/properties/equations/base.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def fit(self,
        T: FloatVectorLike,
        Y: FloatVectorLike,
        sigmaY: Optional[FloatVectorLike] = None,
        fit_only: Optional[list[str]] = None,
        logY: bool = False,
        plot: bool = True,
        ) -> dict:
    """Fit equation to data using non-linear regression.

    Parameters
    ----------
    T : FloatVector
        Temperature. Unit = K.
    Y : FloatVector
        Property to be fitted. Unit = Any.
    sigmaY : FloatVector | None
        Standard deviation of Y. Unit = [Y].
    fit_only : list[str] | None
        List with name of parameters to be fitted.
    logY : bool
        If `True`, the fit will be done in terms of log(Y).
    plot : bool
        If `True` a plot comparing data and fitted correlation will be
        generated.

    Returns
    -------
    dict
        A dictionary of results with the following keys: 'success',
        'parameters', 'covariance', and 'plot'.
    """

    # Current parameter values
    pdict = self.p.copy()

    # Select parameters to be fitted
    pnames_fit = [name for name, info in self._pinfo.items() if info[1]]
    if fit_only:
        pnames_fit = set(fit_only) & set(pnames_fit)
    p0 = [pdict[pname] for pname in pnames_fit]

    # Fit function
    def ffit(x, *p):
        for pname, pvalue in zip(pnames_fit, p):
            pdict[pname] = pvalue
        Yfit = self.equation(T=x, **pdict)
        if logY:
            Yfit = log(Yfit)
        return Yfit

    solution = curve_fit(ffit,
                         xdata=T,
                         ydata=log(Y) if logY else Y,
                         p0=p0,
                         sigma=sigmaY,
                         absolute_sigma=False,
                         full_output=True)
    result = {}
    result['success'] = bool(solution[4])
    if solution[4]:
        popt = solution[0]
        pcov = solution[1]
        print("Fit successful.")
        for pname, pvalue in zip(pnames_fit, popt):
            print(f"{pname}: {pvalue}")
        print("Covariance:")
        print(pcov)
        result['covariance'] = pcov

        # Update attributes
        self.Trange = (min(T), max(T))
        for pname, pvalue in zip(pnames_fit, popt):
            self.p[pname] = pvalue
        result['parameters'] = pdict

        # plot
        if plot:
            kind = 'semilogy' if logY else 'linear'
            fig, ax = self.plot(kind=kind, return_objects=True)  # ok
            ax.plot(T, Y, 'o', mfc='none')
            result['plot'] = (fig, ax)
    else:
        print("Fit error: ", solution[3])
        result['message'] = solution[3]

    return result

plot ¤

plot(
    kind: Literal[
        "linear", "semilogy", "Arrhenius"
    ] = "linear",
    Trange: Optional[tuple[float, float]] = None,
    Tunit: Literal["C", "K"] = "K",
    title: Optional[str] = None,
    axes: Optional[Axes] = None,
    return_objects: bool = False,
) -> Optional[tuple[Optional[Figure], Axes]]

Plot quantity as a function of temperature.

PARAMETER DESCRIPTION
kind

Kind of plot to be generated.

TYPE: Literal['linear', 'semilogy', 'Arrhenius'] DEFAULT: 'linear'

Trange

Temperature range for x-axis. If None, the validity range (Tmin, Tmax) will be used. If no validity range was defined, the range will default to 0-100°C.

TYPE: tuple[float, float] | None DEFAULT: None

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

title

Title of plot. If None, the object name will be used.

TYPE: str | None DEFAULT: None

axes

Matplotlib Axes object.

TYPE: Axes | None DEFAULT: None

return_objects

If True, the Figure and Axes objects are returned (for saving or further manipulations).

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
tuple[Figure | None, Axes] | None

Figure and Axes objects if return_objects is True.

Source code in src/polykin/properties/equations/base.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def plot(self,
         kind: Literal['linear', 'semilogy', 'Arrhenius'] = 'linear',
         Trange: Optional[tuple[float, float]] = None,
         Tunit: Literal['C', 'K'] = 'K',
         title: Optional[str] = None,
         axes: Optional[Axes] = None,
         return_objects: bool = False
         ) -> Optional[tuple[Optional[Figure], Axes]]:
    """Plot quantity as a function of temperature.

    Parameters
    ----------
    kind : Literal['linear', 'semilogy', 'Arrhenius']
        Kind of plot to be generated.
    Trange : tuple[float, float] | None
        Temperature range for x-axis. If `None`, the validity range
        (Tmin, Tmax) will be used. If no validity range was defined, the
        range will default to 0-100°C.
    Tunit : Literal['C', 'K']
        Temperature unit.
    title : str | None
        Title of plot. If `None`, the object name will be used.
    axes : Axes | None
        Matplotlib Axes object.
    return_objects : bool
        If `True`, the Figure and Axes objects are returned (for saving or
        further manipulations).

    Returns
    -------
    tuple[Figure | None, Axes] | None
        Figure and Axes objects if return_objects is `True`.
    """

    # Check inputs
    check_in_set(kind, {'linear', 'semilogy', 'Arrhenius'}, 'kind')
    check_in_set(Tunit, {'K', 'C'}, 'Tunit')
    if Trange is not None:
        Trange_min = 0.
        if Tunit == 'C':
            Trange_min = -273.15
        check_valid_range(Trange, Trange_min, np.inf, 'Trange')

    # Plot objects
    if axes is None:
        fig, ax = plt.subplots()
        if title is None:
            title = self.name
        if title:
            fig.suptitle(title)
        label = None
    else:
        fig = None
        ax = axes
        label = self.name

    # Units and xlabel
    Tunit_range = Tunit
    if kind == 'Arrhenius':
        Tunit = 'K'
    Tsymbol = Tunit
    if Tunit == 'C':
        Tsymbol = '°' + Tunit

    if kind == 'Arrhenius':
        xlabel = r"$1/T$ [" + Tsymbol + r"$^{-1}$]"
    else:
        xlabel = fr"$T$ [{Tsymbol}]"

    # ylabel
    ylabel = fr"${self.symbol}$ [{self.unit}]"
    if axes is not None:
        ylabel0 = ax.get_ylabel()
        if ylabel0 and ylabel not in ylabel0:
            ylabel = ylabel0 + ", " + ylabel

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True)

    # x-axis vector
    if Trange is not None:
        if Tunit_range == 'C':
            Trange = (Trange[0]+273.15, Trange[1]+273.15)
    else:
        Trange = (np.min(self.Trange[0]), np.max(self.Trange[1]))
        if Trange == (0.0, np.inf):
            Trange = (273.15, 373.15)

    try:
        shape = self._shape
    except AttributeError:
        shape = None
    if shape is not None:
        print("Plot method not yet implemented for array-like equations.")
    else:
        TK = np.linspace(*Trange, 100)
        y = self.__call__(TK, 'K')
        T = TK
        if Tunit == 'C':
            T -= 273.15
        if kind == 'linear':
            ax.plot(T, y, label=label)
        elif kind == 'semilogy':
            ax.semilogy(T, y, label=label)
        elif kind == 'Arrhenius':
            ax.semilogy(1/TK, y, label=label)

    if fig is None:
        ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")

    if return_objects:
        return (fig, ax)

Flory ¤

Flory equation of state for the specific volume of a polymer.

This EoS implements the following implicit PVT dependence:

\[ \frac{\tilde{P}\tilde{V}}{\tilde{T}} = \frac{\tilde{V}^{1/3}}{\tilde{V}^{1/3}-1}-\frac{1}{\tilde{V}\tilde{T}}\]

where \(\tilde{V}=V/V^*\), \(\tilde{P}=P/P^*\) and \(\tilde{T}=T/T^*\) are, respectively, the reduced volume, reduced pressure and reduced temperature. \(V^*\), \(P^*\) and \(T^*\) are reference quantities that are polymer dependent.

References

  • Caruthers et al. Handbook of Diffusion and Thermal Properties of Polymers and Polymer Solutions. AIChE, 1998.
PARAMETER DESCRIPTION
V0

Reference volume, \(V^*\).

TYPE: float

T0

Reference temperature, \(T^*\).

TYPE: float

P0

Reference pressure, \(P^*\).

TYPE: float

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

Pmin

Lower pressure bound. Unit = Pa.

TYPE: float DEFAULT: 0.0

Pmax

Upper pressure bound. Unit = Pa.

TYPE: float DEFAULT: inf

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/pvt_polymer/eos.py
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
class Flory(PolymerPVTEoS):
    r"""Flory equation of state for the specific volume of a polymer.

    This EoS implements the following implicit PVT dependence:

    $$ \frac{\tilde{P}\tilde{V}}{\tilde{T}} =
      \frac{\tilde{V}^{1/3}}{\tilde{V}^{1/3}-1}-\frac{1}{\tilde{V}\tilde{T}}$$

    where $\tilde{V}=V/V^*$, $\tilde{P}=P/P^*$ and $\tilde{T}=T/T^*$ are,
    respectively, the reduced volume, reduced pressure and reduced temperature.
    $V^*$, $P^*$ and $T^*$ are reference quantities that are polymer dependent.

    **References**

    *   Caruthers et al. Handbook of Diffusion and Thermal Properties of
        Polymers and Polymer Solutions. AIChE, 1998.

    Parameters
    ----------
    V0 : float
        Reference volume, $V^*$.
    T0 : float
        Reference temperature, $T^*$.
    P0 : float
        Reference pressure, $P^*$.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    Pmin : float
        Lower pressure bound.
        Unit = Pa.
    Pmax : float
        Upper pressure bound.
        Unit = Pa.
    name : str
        Name.
    """

    @staticmethod
    def equation(v: float,
                 t: float,
                 p: float
                 ) -> tuple[float, float, float]:
        r"""Flory equation of state and its volume derivatives.

        Parameters
        ----------
        v : float
            Reduced volume, $\tilde{V}$.
        t : float
            Reduced temperature, $\tilde{T}$.
        p : float
            Reduced pressure, $\tilde{P}$.

        Returns
        -------
        tuple[float, float, float]
            Equation of state, first derivative, second derivative.
        """
        f = p*v/t - (v**(1/3)/(v**(1/3) - 1) - 1/(v*t))  # =0
        d1f = p/t - 1/(t*v**2) - 1/(3*(v**(1/3) - 1)*v**(2/3)) + \
            1/(3*(v**(1/3) - 1)**2*v**(1/3))
        d2f = (2*(9/t + (v**(4/3) - 2*v**(5/3))/(-1 + v**(1/3))**3))/(9*v**3)
        return (f, d1f, d2f)

V ¤

V(
    T: Union[float, FloatArrayLike],
    P: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
    Punit: Literal["bar", "MPa", "Pa"] = "Pa",
) -> Union[float, FloatArray]

Evaluate the specific volume, \(\hat{V}\), at given temperature and pressure, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

P

Pressure. Unit = Punit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

Punit

Pressure unit.

TYPE: Literal['bar', 'MPa', 'Pa'] DEFAULT: 'Pa'

RETURNS DESCRIPTION
float | FloatArray

Specific volume. Unit = m³/kg.

Source code in src/polykin/properties/pvt_polymer/base.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def V(self,
      T: Union[float, FloatArrayLike],
      P: Union[float, FloatArrayLike],
      Tunit: Literal['C', 'K'] = 'K',
      Punit: Literal['bar', 'MPa', 'Pa'] = 'Pa'
      ) -> Union[float, FloatArray]:
    r"""Evaluate the specific volume, $\hat{V}$, at given temperature and
    pressure, including unit conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    P : float | FloatArrayLike
        Pressure.
        Unit = `Punit`.
    Tunit : Literal['C', 'K']
        Temperature unit.
    Punit : Literal['bar', 'MPa', 'Pa']
        Pressure unit.

    Returns
    -------
    float | FloatArray
        Specific volume.
        Unit = m³/kg.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    Pa = convert_check_pressure(P, Punit, self.Prange)
    return self.eval(TK, Pa)

alpha ¤

alpha(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Calculate thermal expansion coefficient, \(\alpha\).

\[\alpha=\frac{1}{V}\left(\frac{\partial V}{\partial T}\right)_{P}\]
PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Thermal expansion coefficient, \(\alpha\). Unit = 1/K.

Source code in src/polykin/properties/pvt_polymer/eos.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def alpha(self,
          T: Union[float, FloatArray],
          P: Union[float, FloatArray]
          ) -> Union[float, FloatArray]:
    r"""Calculate thermal expansion coefficient, $\alpha$.

    $$\alpha=\frac{1}{V}\left(\frac{\partial V}{\partial T}\right)_{P}$$

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Thermal expansion coefficient, $\alpha$.
        Unit = 1/K.
    """
    dT = 0.5
    V2 = self.eval(T + dT, P)
    V1 = self.eval(T - dT, P)
    return (V2 - V1)/dT/(V1 + V2)

beta ¤

beta(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Calculate isothermal compressibility coefficient, \(\beta\).

\[\beta=-\frac{1}{V}\left(\frac{\partial V}{\partial P}\right)_{T}\]
PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Isothermal compressibility coefficient, \(\beta\). Unit = 1/Pa.

Source code in src/polykin/properties/pvt_polymer/eos.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def beta(self,
         T: Union[float, FloatArray],
         P: Union[float, FloatArray]
         ) -> Union[float, FloatArray]:
    r"""Calculate isothermal compressibility coefficient, $\beta$.

    $$\beta=-\frac{1}{V}\left(\frac{\partial V}{\partial P}\right)_{T}$$

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Isothermal compressibility coefficient, $\beta$.
        Unit = 1/Pa.
    """
    dP = 1e5
    P2 = P + dP
    P1 = np.max(P - dP, 0)
    V2 = self.eval(T, P2)
    V1 = self.eval(T, P1)
    return -(V2 - V1)/(P2 - P1)/(V1 + V2)*2

equation staticmethod ¤

equation(
    v: float, t: float, p: float
) -> tuple[float, float, float]

Flory equation of state and its volume derivatives.

PARAMETER DESCRIPTION
v

Reduced volume, \(\tilde{V}\).

TYPE: float

t

Reduced temperature, \(\tilde{T}\).

TYPE: float

p

Reduced pressure, \(\tilde{P}\).

TYPE: float

RETURNS DESCRIPTION
tuple[float, float, float]

Equation of state, first derivative, second derivative.

Source code in src/polykin/properties/pvt_polymer/eos.py
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
@staticmethod
def equation(v: float,
             t: float,
             p: float
             ) -> tuple[float, float, float]:
    r"""Flory equation of state and its volume derivatives.

    Parameters
    ----------
    v : float
        Reduced volume, $\tilde{V}$.
    t : float
        Reduced temperature, $\tilde{T}$.
    p : float
        Reduced pressure, $\tilde{P}$.

    Returns
    -------
    tuple[float, float, float]
        Equation of state, first derivative, second derivative.
    """
    f = p*v/t - (v**(1/3)/(v**(1/3) - 1) - 1/(v*t))  # =0
    d1f = p/t - 1/(t*v**2) - 1/(3*(v**(1/3) - 1)*v**(2/3)) + \
        1/(3*(v**(1/3) - 1)**2*v**(1/3))
    d2f = (2*(9/t + (v**(4/3) - 2*v**(5/3))/(-1 + v**(1/3))**3))/(9*v**3)
    return (f, d1f, d2f)

eval ¤

eval(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Evaluate specific volume, \(\hat{V}\), at given SI conditions without unit conversions or checks.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Specific volume. Unit = m³/kg.

Source code in src/polykin/properties/pvt_polymer/eos.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
@vectorize
def eval(self,
         T: Union[float, FloatArray],
         P: Union[float, FloatArray]
         ) -> Union[float, FloatArray]:
    r"""Evaluate specific volume, $\hat{V}$, at given SI conditions without
    unit conversions or checks.

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Specific volume.
        Unit = m³/kg.
    """
    t = T/self.T0
    p = P/self.P0
    solution = root_scalar(f=self.equation,
                           args=(t, p),
                           # bracket=[1.1, 1.5],
                           x0=1.05,
                           method='halley',
                           fprime=True,
                           fprime2=True)

    if solution.converged:
        v = solution.root
        V = v*self.V0
    else:
        print(solution.flag)
        V = -1.
    return V

from_database classmethod ¤

from_database(name: str) -> Optional[PolymerPVTEquation]

Construct PolymerPVTEquation with parameters from the database.

PARAMETER DESCRIPTION
name

Polymer code name.

TYPE: str

Source code in src/polykin/properties/pvt_polymer/base.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
@classmethod
def from_database(cls,
                  name: str
                  ) -> Optional[PolymerPVTEquation]:
    r"""Construct `PolymerPVTEquation` with parameters from the database.

    Parameters
    ----------
    name : str
        Polymer code name.
    """
    table = load_PVT_parameters(method=cls.__name__)
    try:
        mask = table.index == name
        parameters = table[mask].iloc[0, :].to_dict()
        return cls(**parameters, name=name)
    except IndexError:
        print(
            f"Error: '{name}' does not exist in polymer database.\n"
            f"Valid names are: {table.index.to_list()}")

get_database classmethod ¤

get_database() -> pd.DataFrame

Get database with parameters for the respective PVT equation.

Method Reference
Flory [2] Table 4.1.7 (p. 72-73)
Hartmann-Haque [2] Table 4.1.11 (p. 85-86)
Sanchez-Lacombe [2] Table 4.1.9 (p. 78-79)
Tait [1] Table 3B-1 (p. 41)

References

  1. Danner, Ronald P., and Martin S. High. Handbook of polymer solution thermodynamics. John Wiley & Sons, 2010.
  2. Caruthers et al. Handbook of Diffusion and Thermal Properties of Polymers and Polymer Solutions. AIChE, 1998.
Source code in src/polykin/properties/pvt_polymer/base.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
@classmethod
def get_database(cls) -> pd.DataFrame:
    r"""Get database with parameters for the respective PVT equation.

    | Method          | Reference                            |
    | :-----------    | ------------------------------------ |
    | Flory           | [2] Table 4.1.7  (p. 72-73)          |
    | Hartmann-Haque  | [2] Table 4.1.11 (p. 85-86)          |
    | Sanchez-Lacombe | [2] Table 4.1.9  (p. 78-79)          |
    | Tait            | [1] Table 3B-1 (p. 41)               |

    **References**

    1.  Danner, Ronald P., and Martin S. High. Handbook of polymer
        solution thermodynamics. John Wiley & Sons, 2010.
    2.  Caruthers et al. Handbook of Diffusion and Thermal Properties of
        Polymers and Polymer Solutions. AIChE, 1998.
    """
    return load_PVT_parameters(method=cls.__name__)

HartmannHaque ¤

Hartmann-Haque equation of state for the specific volume of a polymer.

This EoS implements the following implicit PVT dependence:

\[ \tilde{P}\tilde{V}^5=\tilde{T}^{3/2}-\ln{\tilde{V}} \]

where \(\tilde{V}=V/V^*\), \(\tilde{P}=P/P^*\) and \(\tilde{T}=T/T^*\) are, respectively, the reduced volume, reduced pressure and reduced temperature. \(V^*\), \(P^*\) and \(T^*\) are reference quantities that are polymer dependent.

References

  • Caruthers et al. Handbook of Diffusion and Thermal Properties of Polymers and Polymer Solutions. AIChE, 1998.
PARAMETER DESCRIPTION
V0

Reference volume, \(V^*\).

TYPE: float

T0

Reference temperature, \(T^*\).

TYPE: float

P0

Reference pressure, \(P^*\).

TYPE: float

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

Pmin

Lower pressure bound. Unit = Pa.

TYPE: float DEFAULT: 0.0

Pmax

Upper pressure bound. Unit = Pa.

TYPE: float DEFAULT: inf

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/pvt_polymer/eos.py
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
class HartmannHaque(PolymerPVTEoS):
    r"""Hartmann-Haque equation of state for the specific volume of a polymer.

    This EoS implements the following implicit PVT dependence:

    $$ \tilde{P}\tilde{V}^5=\tilde{T}^{3/2}-\ln{\tilde{V}} $$

    where $\tilde{V}=V/V^*$, $\tilde{P}=P/P^*$ and $\tilde{T}=T/T^*$ are,
    respectively, the reduced volume, reduced pressure and reduced temperature.
    $V^*$, $P^*$ and $T^*$ are reference quantities that are polymer dependent.

    **References**

    *   Caruthers et al. Handbook of Diffusion and Thermal Properties of
        Polymers and Polymer Solutions. AIChE, 1998.

    Parameters
    ----------
    V0 : float
        Reference volume, $V^*$.
    T0 : float
        Reference temperature, $T^*$.
    P0 : float
        Reference pressure, $P^*$.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    Pmin : float
        Lower pressure bound.
        Unit = Pa.
    Pmax : float
        Upper pressure bound.
        Unit = Pa.
    name : str
        Name.
    """

    @staticmethod
    def equation(v: float,
                 t: float,
                 p: float
                 ) -> tuple[float, float, float]:
        """Hartmann-Haque equation of state and its volume derivatives.

        Parameters
        ----------
        v : float
            Reduced volume.
        t : float
            Reduced temperature.
        p : float
            Reduced pressure.

        Returns
        -------
        tuple[float, float, float]
            Equation of state, first derivative, second derivative.
        """
        f = p*v**5 - t**(3/2) + log(v)  # =0
        d1f = 5*p*v**4 + 1/v
        d2f = 20*p*v**3 - 1/v**2
        return (f, d1f, d2f)

V ¤

V(
    T: Union[float, FloatArrayLike],
    P: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
    Punit: Literal["bar", "MPa", "Pa"] = "Pa",
) -> Union[float, FloatArray]

Evaluate the specific volume, \(\hat{V}\), at given temperature and pressure, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

P

Pressure. Unit = Punit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

Punit

Pressure unit.

TYPE: Literal['bar', 'MPa', 'Pa'] DEFAULT: 'Pa'

RETURNS DESCRIPTION
float | FloatArray

Specific volume. Unit = m³/kg.

Source code in src/polykin/properties/pvt_polymer/base.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def V(self,
      T: Union[float, FloatArrayLike],
      P: Union[float, FloatArrayLike],
      Tunit: Literal['C', 'K'] = 'K',
      Punit: Literal['bar', 'MPa', 'Pa'] = 'Pa'
      ) -> Union[float, FloatArray]:
    r"""Evaluate the specific volume, $\hat{V}$, at given temperature and
    pressure, including unit conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    P : float | FloatArrayLike
        Pressure.
        Unit = `Punit`.
    Tunit : Literal['C', 'K']
        Temperature unit.
    Punit : Literal['bar', 'MPa', 'Pa']
        Pressure unit.

    Returns
    -------
    float | FloatArray
        Specific volume.
        Unit = m³/kg.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    Pa = convert_check_pressure(P, Punit, self.Prange)
    return self.eval(TK, Pa)

alpha ¤

alpha(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Calculate thermal expansion coefficient, \(\alpha\).

\[\alpha=\frac{1}{V}\left(\frac{\partial V}{\partial T}\right)_{P}\]
PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Thermal expansion coefficient, \(\alpha\). Unit = 1/K.

Source code in src/polykin/properties/pvt_polymer/eos.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def alpha(self,
          T: Union[float, FloatArray],
          P: Union[float, FloatArray]
          ) -> Union[float, FloatArray]:
    r"""Calculate thermal expansion coefficient, $\alpha$.

    $$\alpha=\frac{1}{V}\left(\frac{\partial V}{\partial T}\right)_{P}$$

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Thermal expansion coefficient, $\alpha$.
        Unit = 1/K.
    """
    dT = 0.5
    V2 = self.eval(T + dT, P)
    V1 = self.eval(T - dT, P)
    return (V2 - V1)/dT/(V1 + V2)

beta ¤

beta(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Calculate isothermal compressibility coefficient, \(\beta\).

\[\beta=-\frac{1}{V}\left(\frac{\partial V}{\partial P}\right)_{T}\]
PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Isothermal compressibility coefficient, \(\beta\). Unit = 1/Pa.

Source code in src/polykin/properties/pvt_polymer/eos.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def beta(self,
         T: Union[float, FloatArray],
         P: Union[float, FloatArray]
         ) -> Union[float, FloatArray]:
    r"""Calculate isothermal compressibility coefficient, $\beta$.

    $$\beta=-\frac{1}{V}\left(\frac{\partial V}{\partial P}\right)_{T}$$

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Isothermal compressibility coefficient, $\beta$.
        Unit = 1/Pa.
    """
    dP = 1e5
    P2 = P + dP
    P1 = np.max(P - dP, 0)
    V2 = self.eval(T, P2)
    V1 = self.eval(T, P1)
    return -(V2 - V1)/(P2 - P1)/(V1 + V2)*2

equation staticmethod ¤

equation(
    v: float, t: float, p: float
) -> tuple[float, float, float]

Hartmann-Haque equation of state and its volume derivatives.

PARAMETER DESCRIPTION
v

Reduced volume.

TYPE: float

t

Reduced temperature.

TYPE: float

p

Reduced pressure.

TYPE: float

RETURNS DESCRIPTION
tuple[float, float, float]

Equation of state, first derivative, second derivative.

Source code in src/polykin/properties/pvt_polymer/eos.py
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
@staticmethod
def equation(v: float,
             t: float,
             p: float
             ) -> tuple[float, float, float]:
    """Hartmann-Haque equation of state and its volume derivatives.

    Parameters
    ----------
    v : float
        Reduced volume.
    t : float
        Reduced temperature.
    p : float
        Reduced pressure.

    Returns
    -------
    tuple[float, float, float]
        Equation of state, first derivative, second derivative.
    """
    f = p*v**5 - t**(3/2) + log(v)  # =0
    d1f = 5*p*v**4 + 1/v
    d2f = 20*p*v**3 - 1/v**2
    return (f, d1f, d2f)

eval ¤

eval(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Evaluate specific volume, \(\hat{V}\), at given SI conditions without unit conversions or checks.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Specific volume. Unit = m³/kg.

Source code in src/polykin/properties/pvt_polymer/eos.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
@vectorize
def eval(self,
         T: Union[float, FloatArray],
         P: Union[float, FloatArray]
         ) -> Union[float, FloatArray]:
    r"""Evaluate specific volume, $\hat{V}$, at given SI conditions without
    unit conversions or checks.

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Specific volume.
        Unit = m³/kg.
    """
    t = T/self.T0
    p = P/self.P0
    solution = root_scalar(f=self.equation,
                           args=(t, p),
                           # bracket=[1.1, 1.5],
                           x0=1.05,
                           method='halley',
                           fprime=True,
                           fprime2=True)

    if solution.converged:
        v = solution.root
        V = v*self.V0
    else:
        print(solution.flag)
        V = -1.
    return V

from_database classmethod ¤

from_database(name: str) -> Optional[PolymerPVTEquation]

Construct PolymerPVTEquation with parameters from the database.

PARAMETER DESCRIPTION
name

Polymer code name.

TYPE: str

Source code in src/polykin/properties/pvt_polymer/base.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
@classmethod
def from_database(cls,
                  name: str
                  ) -> Optional[PolymerPVTEquation]:
    r"""Construct `PolymerPVTEquation` with parameters from the database.

    Parameters
    ----------
    name : str
        Polymer code name.
    """
    table = load_PVT_parameters(method=cls.__name__)
    try:
        mask = table.index == name
        parameters = table[mask].iloc[0, :].to_dict()
        return cls(**parameters, name=name)
    except IndexError:
        print(
            f"Error: '{name}' does not exist in polymer database.\n"
            f"Valid names are: {table.index.to_list()}")

get_database classmethod ¤

get_database() -> pd.DataFrame

Get database with parameters for the respective PVT equation.

Method Reference
Flory [2] Table 4.1.7 (p. 72-73)
Hartmann-Haque [2] Table 4.1.11 (p. 85-86)
Sanchez-Lacombe [2] Table 4.1.9 (p. 78-79)
Tait [1] Table 3B-1 (p. 41)

References

  1. Danner, Ronald P., and Martin S. High. Handbook of polymer solution thermodynamics. John Wiley & Sons, 2010.
  2. Caruthers et al. Handbook of Diffusion and Thermal Properties of Polymers and Polymer Solutions. AIChE, 1998.
Source code in src/polykin/properties/pvt_polymer/base.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
@classmethod
def get_database(cls) -> pd.DataFrame:
    r"""Get database with parameters for the respective PVT equation.

    | Method          | Reference                            |
    | :-----------    | ------------------------------------ |
    | Flory           | [2] Table 4.1.7  (p. 72-73)          |
    | Hartmann-Haque  | [2] Table 4.1.11 (p. 85-86)          |
    | Sanchez-Lacombe | [2] Table 4.1.9  (p. 78-79)          |
    | Tait            | [1] Table 3B-1 (p. 41)               |

    **References**

    1.  Danner, Ronald P., and Martin S. High. Handbook of polymer
        solution thermodynamics. John Wiley & Sons, 2010.
    2.  Caruthers et al. Handbook of Diffusion and Thermal Properties of
        Polymers and Polymer Solutions. AIChE, 1998.
    """
    return load_PVT_parameters(method=cls.__name__)

SanchezLacombe ¤

Sanchez-Lacombe equation of state for the specific volume of a polymer.

This EoS implements the following implicit PVT dependence:

\[ \frac{1}{\tilde{V}^2} + \tilde{P} + \tilde{T}\left [ \ln\left ( 1-\frac{1}{\tilde{V}} \right ) + \frac{1}{\tilde{V}} \right ]=0 \]

where \(\tilde{V}=V/V^*\), \(\tilde{P}=P/P^*\) and \(\tilde{T}=T/T^*\) are, respectively, the reduced volume, reduced pressure and reduced temperature. \(V^*\), \(P^*\) and \(T^*\) are reference quantities that are polymer dependent.

References

  • Caruthers et al. Handbook of Diffusion and Thermal Properties of Polymers and Polymer Solutions. AIChE, 1998.
PARAMETER DESCRIPTION
V0

Reference volume, \(V^*\).

TYPE: float

T0

Reference temperature, \(T^*\).

TYPE: float

P0

Reference pressure, \(P^*\).

TYPE: float

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

Pmin

Lower pressure bound. Unit = Pa.

TYPE: float DEFAULT: 0.0

Pmax

Upper pressure bound. Unit = Pa.

TYPE: float DEFAULT: inf

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/pvt_polymer/eos.py
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
class SanchezLacombe(PolymerPVTEoS):
    r"""Sanchez-Lacombe equation of state for the specific volume of a polymer.

    This EoS implements the following implicit PVT dependence:

    $$ \frac{1}{\tilde{V}^2} + \tilde{P} +
        \tilde{T}\left [ \ln\left ( 1-\frac{1}{\tilde{V}} \right ) +
        \frac{1}{\tilde{V}} \right ]=0 $$

    where $\tilde{V}=V/V^*$, $\tilde{P}=P/P^*$ and $\tilde{T}=T/T^*$ are,
    respectively, the reduced volume, reduced pressure and reduced temperature.
    $V^*$, $P^*$ and $T^*$ are reference quantities that are polymer dependent.

    **References**

    *   Caruthers et al. Handbook of Diffusion and Thermal Properties of
        Polymers and Polymer Solutions. AIChE, 1998.

    Parameters
    ----------
    V0 : float
        Reference volume, $V^*$.
    T0 : float
        Reference temperature, $T^*$.
    P0 : float
        Reference pressure, $P^*$.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    Pmin : float
        Lower pressure bound.
        Unit = Pa.
    Pmax : float
        Upper pressure bound.
        Unit = Pa.
    name : str
        Name.
    """

    @staticmethod
    def equation(v: float,
                 t: float,
                 p: float
                 ) -> tuple[float, float, float]:
        """Sanchez-Lacombe equation of state and its volume derivatives.

        Parameters
        ----------
        v : float
            Reduced volume.
        t : float
            Reduced temperature.
        p : float
            Reduced pressure.

        Returns
        -------
        tuple[float, float, float]
            Equation of state, first derivative, second derivative.
        """
        f = 1/v**2 + p + t*(log(1 - 1/v) + 1/v)  # =0
        d1f = ((t - 2)*v + 2)/((v - 1)*v**3)
        d2f = (-3*(t - 2)*v**2 + 2*(t - 6)*v + 6)/((v - 1)**2*v**4)
        return (f, d1f, d2f)

V ¤

V(
    T: Union[float, FloatArrayLike],
    P: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
    Punit: Literal["bar", "MPa", "Pa"] = "Pa",
) -> Union[float, FloatArray]

Evaluate the specific volume, \(\hat{V}\), at given temperature and pressure, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

P

Pressure. Unit = Punit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

Punit

Pressure unit.

TYPE: Literal['bar', 'MPa', 'Pa'] DEFAULT: 'Pa'

RETURNS DESCRIPTION
float | FloatArray

Specific volume. Unit = m³/kg.

Source code in src/polykin/properties/pvt_polymer/base.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def V(self,
      T: Union[float, FloatArrayLike],
      P: Union[float, FloatArrayLike],
      Tunit: Literal['C', 'K'] = 'K',
      Punit: Literal['bar', 'MPa', 'Pa'] = 'Pa'
      ) -> Union[float, FloatArray]:
    r"""Evaluate the specific volume, $\hat{V}$, at given temperature and
    pressure, including unit conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    P : float | FloatArrayLike
        Pressure.
        Unit = `Punit`.
    Tunit : Literal['C', 'K']
        Temperature unit.
    Punit : Literal['bar', 'MPa', 'Pa']
        Pressure unit.

    Returns
    -------
    float | FloatArray
        Specific volume.
        Unit = m³/kg.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    Pa = convert_check_pressure(P, Punit, self.Prange)
    return self.eval(TK, Pa)

alpha ¤

alpha(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Calculate thermal expansion coefficient, \(\alpha\).

\[\alpha=\frac{1}{V}\left(\frac{\partial V}{\partial T}\right)_{P}\]
PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Thermal expansion coefficient, \(\alpha\). Unit = 1/K.

Source code in src/polykin/properties/pvt_polymer/eos.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def alpha(self,
          T: Union[float, FloatArray],
          P: Union[float, FloatArray]
          ) -> Union[float, FloatArray]:
    r"""Calculate thermal expansion coefficient, $\alpha$.

    $$\alpha=\frac{1}{V}\left(\frac{\partial V}{\partial T}\right)_{P}$$

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Thermal expansion coefficient, $\alpha$.
        Unit = 1/K.
    """
    dT = 0.5
    V2 = self.eval(T + dT, P)
    V1 = self.eval(T - dT, P)
    return (V2 - V1)/dT/(V1 + V2)

beta ¤

beta(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Calculate isothermal compressibility coefficient, \(\beta\).

\[\beta=-\frac{1}{V}\left(\frac{\partial V}{\partial P}\right)_{T}\]
PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Isothermal compressibility coefficient, \(\beta\). Unit = 1/Pa.

Source code in src/polykin/properties/pvt_polymer/eos.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def beta(self,
         T: Union[float, FloatArray],
         P: Union[float, FloatArray]
         ) -> Union[float, FloatArray]:
    r"""Calculate isothermal compressibility coefficient, $\beta$.

    $$\beta=-\frac{1}{V}\left(\frac{\partial V}{\partial P}\right)_{T}$$

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Isothermal compressibility coefficient, $\beta$.
        Unit = 1/Pa.
    """
    dP = 1e5
    P2 = P + dP
    P1 = np.max(P - dP, 0)
    V2 = self.eval(T, P2)
    V1 = self.eval(T, P1)
    return -(V2 - V1)/(P2 - P1)/(V1 + V2)*2

equation staticmethod ¤

equation(
    v: float, t: float, p: float
) -> tuple[float, float, float]

Sanchez-Lacombe equation of state and its volume derivatives.

PARAMETER DESCRIPTION
v

Reduced volume.

TYPE: float

t

Reduced temperature.

TYPE: float

p

Reduced pressure.

TYPE: float

RETURNS DESCRIPTION
tuple[float, float, float]

Equation of state, first derivative, second derivative.

Source code in src/polykin/properties/pvt_polymer/eos.py
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
@staticmethod
def equation(v: float,
             t: float,
             p: float
             ) -> tuple[float, float, float]:
    """Sanchez-Lacombe equation of state and its volume derivatives.

    Parameters
    ----------
    v : float
        Reduced volume.
    t : float
        Reduced temperature.
    p : float
        Reduced pressure.

    Returns
    -------
    tuple[float, float, float]
        Equation of state, first derivative, second derivative.
    """
    f = 1/v**2 + p + t*(log(1 - 1/v) + 1/v)  # =0
    d1f = ((t - 2)*v + 2)/((v - 1)*v**3)
    d2f = (-3*(t - 2)*v**2 + 2*(t - 6)*v + 6)/((v - 1)**2*v**4)
    return (f, d1f, d2f)

eval ¤

eval(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Evaluate specific volume, \(\hat{V}\), at given SI conditions without unit conversions or checks.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Specific volume. Unit = m³/kg.

Source code in src/polykin/properties/pvt_polymer/eos.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
@vectorize
def eval(self,
         T: Union[float, FloatArray],
         P: Union[float, FloatArray]
         ) -> Union[float, FloatArray]:
    r"""Evaluate specific volume, $\hat{V}$, at given SI conditions without
    unit conversions or checks.

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Specific volume.
        Unit = m³/kg.
    """
    t = T/self.T0
    p = P/self.P0
    solution = root_scalar(f=self.equation,
                           args=(t, p),
                           # bracket=[1.1, 1.5],
                           x0=1.05,
                           method='halley',
                           fprime=True,
                           fprime2=True)

    if solution.converged:
        v = solution.root
        V = v*self.V0
    else:
        print(solution.flag)
        V = -1.
    return V

from_database classmethod ¤

from_database(name: str) -> Optional[PolymerPVTEquation]

Construct PolymerPVTEquation with parameters from the database.

PARAMETER DESCRIPTION
name

Polymer code name.

TYPE: str

Source code in src/polykin/properties/pvt_polymer/base.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
@classmethod
def from_database(cls,
                  name: str
                  ) -> Optional[PolymerPVTEquation]:
    r"""Construct `PolymerPVTEquation` with parameters from the database.

    Parameters
    ----------
    name : str
        Polymer code name.
    """
    table = load_PVT_parameters(method=cls.__name__)
    try:
        mask = table.index == name
        parameters = table[mask].iloc[0, :].to_dict()
        return cls(**parameters, name=name)
    except IndexError:
        print(
            f"Error: '{name}' does not exist in polymer database.\n"
            f"Valid names are: {table.index.to_list()}")

get_database classmethod ¤

get_database() -> pd.DataFrame

Get database with parameters for the respective PVT equation.

Method Reference
Flory [2] Table 4.1.7 (p. 72-73)
Hartmann-Haque [2] Table 4.1.11 (p. 85-86)
Sanchez-Lacombe [2] Table 4.1.9 (p. 78-79)
Tait [1] Table 3B-1 (p. 41)

References

  1. Danner, Ronald P., and Martin S. High. Handbook of polymer solution thermodynamics. John Wiley & Sons, 2010.
  2. Caruthers et al. Handbook of Diffusion and Thermal Properties of Polymers and Polymer Solutions. AIChE, 1998.
Source code in src/polykin/properties/pvt_polymer/base.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
@classmethod
def get_database(cls) -> pd.DataFrame:
    r"""Get database with parameters for the respective PVT equation.

    | Method          | Reference                            |
    | :-----------    | ------------------------------------ |
    | Flory           | [2] Table 4.1.7  (p. 72-73)          |
    | Hartmann-Haque  | [2] Table 4.1.11 (p. 85-86)          |
    | Sanchez-Lacombe | [2] Table 4.1.9  (p. 78-79)          |
    | Tait            | [1] Table 3B-1 (p. 41)               |

    **References**

    1.  Danner, Ronald P., and Martin S. High. Handbook of polymer
        solution thermodynamics. John Wiley & Sons, 2010.
    2.  Caruthers et al. Handbook of Diffusion and Thermal Properties of
        Polymers and Polymer Solutions. AIChE, 1998.
    """
    return load_PVT_parameters(method=cls.__name__)

Tait ¤

Tait equation of state for the specific volume of a liquid.

This EoS implements the following explicit PVT dependence:

\[\hat{V}(T,P)=\hat{V}(T,0)\left[1-C\ln\left(\frac{P}{B(T)}\right)\right]\]

with:

\[ \begin{gather*} \hat{V}(T,0) = A_0 + A_1(T - 273.15) + A_2(T - 273.15)^2 \\ B(T) = B_0\exp\left [-B_1(T - 273.15)\right] \end{gather*} \]

where \(A_i\) and \(B_i\) are constant parameters, \(T\) is the absolute temperature, and \(P\) is the pressure.

References

  • Danner, Ronald P., and Martin S. High. Handbook of polymer solution thermodynamics. John Wiley & Sons, 2010.
PARAMETER DESCRIPTION
A0

Parameter of equation. Unit = m³/kg.

TYPE: float

A1

Parameter of equation. Unit = m³/(kg·K).

TYPE: float

A2

Parameter of equation. Unit = m³/(kg·K²).

TYPE: float

B0

Parameter of equation. Unit = Pa.

TYPE: float

B1

Parameter of equation. Unit = 1/K.

TYPE: float

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

Pmin

Lower pressure bound. Unit = Pa.

TYPE: float DEFAULT: 0.0

Pmax

Upper pressure bound. Unit = Pa.

TYPE: float DEFAULT: inf

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/pvt_polymer/tait.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
class Tait(PolymerPVTEquation):
    r"""Tait equation of state for the specific volume of a liquid.

    This EoS implements the following explicit PVT dependence:

    $$\hat{V}(T,P)=\hat{V}(T,0)\left[1-C\ln\left(\frac{P}{B(T)}\right)\right]$$

    with:

    $$ \begin{gather*}
    \hat{V}(T,0) = A_0 + A_1(T - 273.15) + A_2(T - 273.15)^2 \\
    B(T) = B_0\exp\left [-B_1(T - 273.15)\right]
    \end{gather*} $$

    where $A_i$ and $B_i$ are constant parameters, $T$ is the absolute
    temperature, and $P$ is the pressure.

    **References**

    *   Danner, Ronald P., and Martin S. High. Handbook of polymer
        solution thermodynamics. John Wiley & Sons, 2010.

    Parameters
    ----------
    A0 : float
        Parameter of equation.
        Unit = m³/kg.
    A1 : float
        Parameter of equation.
        Unit = m³/(kg·K).
    A2 : float
        Parameter of equation.
        Unit = m³/(kg·K²).
    B0 : float
        Parameter of equation.
        Unit = Pa.
    B1 : float
        Parameter of equation.
        Unit = 1/K.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    Pmin : float
        Lower pressure bound.
        Unit = Pa.
    Pmax : float
        Upper pressure bound.
        Unit = Pa.
    name : str
        Name.
    """

    A0: float
    A1: float
    A2: float
    B0: float
    B1: float

    _C = 0.0894

    def __init__(self,
                 A0: float,
                 A1: float,
                 A2: float,
                 B0: float,
                 B1: float,
                 Tmin: float = 0.0,
                 Tmax: float = np.inf,
                 Pmin: float = 0.0,
                 Pmax: float = np.inf,
                 name: str = ''
                 ) -> None:
        """Construct `Tait` with the given parameters."""

        # Check bounds
        check_bounds(A0, 1e-4, 2e-3, 'A0')
        check_bounds(A1, 1e-7, 2e-6, 'A1')
        check_bounds(A2, -2e-9, 1e-8, 'A2')
        check_bounds(B0, 1e7, 1e9, 'B0')
        check_bounds(B1, 1e-3, 2e-2, 'B1')

        self.A0 = A0
        self.A1 = A1
        self.A2 = A2
        self.B0 = B0
        self.B1 = B1
        super().__init__(Tmin, Tmax, Pmin, Pmax, name)

    def __repr__(self) -> str:
        return (
            f"name:          {self.name}\n"
            f"symbol:        {self.symbol}\n"
            f"unit:          {self.unit}\n"
            f"Trange [K]:    {self.Trange}\n"
            f"Prange [Pa]:   {self.Prange}\n"
            f"A0 [m³/kg]:    {self.A0}\n"
            f"A1 [m³/kg.K]:  {self.A1}\n"
            f"A2 [m³/kg.K²]: {self.A2}\n"
            f"B0 [Pa]:       {self.B0}\n"
            f"B1 [1/K]:      {self.B1}"
        )

    def eval(self,
             T: Union[float, FloatArray],
             P: Union[float, FloatArray]
             ) -> Union[float, FloatArray]:
        r"""Evaluate specific volume, $\hat{V}$, at given SI conditions without
        unit conversions or checks.

        Parameters
        ----------
        T : float | FloatArray
            Temperature.
            Unit = K.
        P : float | FloatArray
            Pressure.
            Unit = Pa.

        Returns
        -------
        float | FloatArray
            Specific volume.
            Unit = m³/kg.
        """
        TC = T - 273.15
        V0 = self.A0 + self.A1*TC + self.A2*TC**2
        B = self._B(T)
        V = V0*(1 - self._C*log(1 + P/B))
        return V

    def _B(self,
           T: Union[float, FloatArray]
           ) -> Union[float, FloatArray]:
        r"""Parameter B(T).

        Parameters
        ----------
        T : float | FloatArray
            Temperature.
            Unit = K.

        Returns
        -------
        float | FloatArray
            B(T).
            Unit = Pa.
        """
        return self.B0*exp(-self.B1*(T - 273.15))

    def alpha(self,
              T: Union[float, FloatArray],
              P: Union[float, FloatArray]
              ) -> Union[float, FloatArray]:
        r"""Calculate thermal expansion coefficient, $\alpha$.

        $$\alpha=\frac{1}{V}\left(\frac{\partial V}{\partial T}\right)_{P}$$

        Parameters
        ----------
        T : float | FloatArray
            Temperature.
            Unit = K.
        P : float | FloatArray
            Pressure.
            Unit = Pa.

        Returns
        -------
        float | FloatArray
            Thermal expansion coefficient, $\alpha$.
            Unit = 1/K.
        """
        A0 = self.A0
        A1 = self.A1
        A2 = self.A2
        TC = T - 273.15
        alpha0 = (A1 + 2*A2*TC)/(A0 + A1*TC + A2*TC**2)
        return alpha0 - P*self.B1*self.beta(T, P)

    def beta(self,
             T: Union[float, FloatArray],
             P: Union[float, FloatArray]
             ) -> Union[float, FloatArray]:
        r"""Calculate isothermal compressibility coefficient, $\beta$.

        $$\beta=-\frac{1}{V}\left(\frac{\partial V}{\partial P}\right)_{T}$$

        Parameters
        ----------
        T : float | FloatArray
            Temperature.
            Unit = K.
        P : float | FloatArray
            Pressure.
            Unit = Pa.

        Returns
        -------
        float | FloatArray
            Isothermal compressibility coefficient, $\beta$.
            Unit = 1/Pa.
        """
        B = self._B(T)
        return (self._C/(P + B))/(1 - self._C*log(1 + P/B))

V ¤

V(
    T: Union[float, FloatArrayLike],
    P: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
    Punit: Literal["bar", "MPa", "Pa"] = "Pa",
) -> Union[float, FloatArray]

Evaluate the specific volume, \(\hat{V}\), at given temperature and pressure, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

P

Pressure. Unit = Punit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

Punit

Pressure unit.

TYPE: Literal['bar', 'MPa', 'Pa'] DEFAULT: 'Pa'

RETURNS DESCRIPTION
float | FloatArray

Specific volume. Unit = m³/kg.

Source code in src/polykin/properties/pvt_polymer/base.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def V(self,
      T: Union[float, FloatArrayLike],
      P: Union[float, FloatArrayLike],
      Tunit: Literal['C', 'K'] = 'K',
      Punit: Literal['bar', 'MPa', 'Pa'] = 'Pa'
      ) -> Union[float, FloatArray]:
    r"""Evaluate the specific volume, $\hat{V}$, at given temperature and
    pressure, including unit conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    P : float | FloatArrayLike
        Pressure.
        Unit = `Punit`.
    Tunit : Literal['C', 'K']
        Temperature unit.
    Punit : Literal['bar', 'MPa', 'Pa']
        Pressure unit.

    Returns
    -------
    float | FloatArray
        Specific volume.
        Unit = m³/kg.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    Pa = convert_check_pressure(P, Punit, self.Prange)
    return self.eval(TK, Pa)

__init__ ¤

__init__(
    A0: float,
    A1: float,
    A2: float,
    B0: float,
    B1: float,
    Tmin: float = 0.0,
    Tmax: float = np.inf,
    Pmin: float = 0.0,
    Pmax: float = np.inf,
    name: str = "",
) -> None

Construct Tait with the given parameters.

Source code in src/polykin/properties/pvt_polymer/tait.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def __init__(self,
             A0: float,
             A1: float,
             A2: float,
             B0: float,
             B1: float,
             Tmin: float = 0.0,
             Tmax: float = np.inf,
             Pmin: float = 0.0,
             Pmax: float = np.inf,
             name: str = ''
             ) -> None:
    """Construct `Tait` with the given parameters."""

    # Check bounds
    check_bounds(A0, 1e-4, 2e-3, 'A0')
    check_bounds(A1, 1e-7, 2e-6, 'A1')
    check_bounds(A2, -2e-9, 1e-8, 'A2')
    check_bounds(B0, 1e7, 1e9, 'B0')
    check_bounds(B1, 1e-3, 2e-2, 'B1')

    self.A0 = A0
    self.A1 = A1
    self.A2 = A2
    self.B0 = B0
    self.B1 = B1
    super().__init__(Tmin, Tmax, Pmin, Pmax, name)

alpha ¤

alpha(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Calculate thermal expansion coefficient, \(\alpha\).

\[\alpha=\frac{1}{V}\left(\frac{\partial V}{\partial T}\right)_{P}\]
PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Thermal expansion coefficient, \(\alpha\). Unit = 1/K.

Source code in src/polykin/properties/pvt_polymer/tait.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
def alpha(self,
          T: Union[float, FloatArray],
          P: Union[float, FloatArray]
          ) -> Union[float, FloatArray]:
    r"""Calculate thermal expansion coefficient, $\alpha$.

    $$\alpha=\frac{1}{V}\left(\frac{\partial V}{\partial T}\right)_{P}$$

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Thermal expansion coefficient, $\alpha$.
        Unit = 1/K.
    """
    A0 = self.A0
    A1 = self.A1
    A2 = self.A2
    TC = T - 273.15
    alpha0 = (A1 + 2*A2*TC)/(A0 + A1*TC + A2*TC**2)
    return alpha0 - P*self.B1*self.beta(T, P)

beta ¤

beta(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Calculate isothermal compressibility coefficient, \(\beta\).

\[\beta=-\frac{1}{V}\left(\frac{\partial V}{\partial P}\right)_{T}\]
PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Isothermal compressibility coefficient, \(\beta\). Unit = 1/Pa.

Source code in src/polykin/properties/pvt_polymer/tait.py
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def beta(self,
         T: Union[float, FloatArray],
         P: Union[float, FloatArray]
         ) -> Union[float, FloatArray]:
    r"""Calculate isothermal compressibility coefficient, $\beta$.

    $$\beta=-\frac{1}{V}\left(\frac{\partial V}{\partial P}\right)_{T}$$

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Isothermal compressibility coefficient, $\beta$.
        Unit = 1/Pa.
    """
    B = self._B(T)
    return (self._C/(P + B))/(1 - self._C*log(1 + P/B))

eval ¤

eval(
    T: Union[float, FloatArray], P: Union[float, FloatArray]
) -> Union[float, FloatArray]

Evaluate specific volume, \(\hat{V}\), at given SI conditions without unit conversions or checks.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

P

Pressure. Unit = Pa.

TYPE: float | FloatArray

RETURNS DESCRIPTION
float | FloatArray

Specific volume. Unit = m³/kg.

Source code in src/polykin/properties/pvt_polymer/tait.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
def eval(self,
         T: Union[float, FloatArray],
         P: Union[float, FloatArray]
         ) -> Union[float, FloatArray]:
    r"""Evaluate specific volume, $\hat{V}$, at given SI conditions without
    unit conversions or checks.

    Parameters
    ----------
    T : float | FloatArray
        Temperature.
        Unit = K.
    P : float | FloatArray
        Pressure.
        Unit = Pa.

    Returns
    -------
    float | FloatArray
        Specific volume.
        Unit = m³/kg.
    """
    TC = T - 273.15
    V0 = self.A0 + self.A1*TC + self.A2*TC**2
    B = self._B(T)
    V = V0*(1 - self._C*log(1 + P/B))
    return V

from_database classmethod ¤

from_database(name: str) -> Optional[PolymerPVTEquation]

Construct PolymerPVTEquation with parameters from the database.

PARAMETER DESCRIPTION
name

Polymer code name.

TYPE: str

Source code in src/polykin/properties/pvt_polymer/base.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
@classmethod
def from_database(cls,
                  name: str
                  ) -> Optional[PolymerPVTEquation]:
    r"""Construct `PolymerPVTEquation` with parameters from the database.

    Parameters
    ----------
    name : str
        Polymer code name.
    """
    table = load_PVT_parameters(method=cls.__name__)
    try:
        mask = table.index == name
        parameters = table[mask].iloc[0, :].to_dict()
        return cls(**parameters, name=name)
    except IndexError:
        print(
            f"Error: '{name}' does not exist in polymer database.\n"
            f"Valid names are: {table.index.to_list()}")

get_database classmethod ¤

get_database() -> pd.DataFrame

Get database with parameters for the respective PVT equation.

Method Reference
Flory [2] Table 4.1.7 (p. 72-73)
Hartmann-Haque [2] Table 4.1.11 (p. 85-86)
Sanchez-Lacombe [2] Table 4.1.9 (p. 78-79)
Tait [1] Table 3B-1 (p. 41)

References

  1. Danner, Ronald P., and Martin S. High. Handbook of polymer solution thermodynamics. John Wiley & Sons, 2010.
  2. Caruthers et al. Handbook of Diffusion and Thermal Properties of Polymers and Polymer Solutions. AIChE, 1998.
Source code in src/polykin/properties/pvt_polymer/base.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
@classmethod
def get_database(cls) -> pd.DataFrame:
    r"""Get database with parameters for the respective PVT equation.

    | Method          | Reference                            |
    | :-----------    | ------------------------------------ |
    | Flory           | [2] Table 4.1.7  (p. 72-73)          |
    | Hartmann-Haque  | [2] Table 4.1.11 (p. 85-86)          |
    | Sanchez-Lacombe | [2] Table 4.1.9  (p. 78-79)          |
    | Tait            | [1] Table 3B-1 (p. 41)               |

    **References**

    1.  Danner, Ronald P., and Martin S. High. Handbook of polymer
        solution thermodynamics. John Wiley & Sons, 2010.
    2.  Caruthers et al. Handbook of Diffusion and Thermal Properties of
        Polymers and Polymer Solutions. AIChE, 1998.
    """
    return load_PVT_parameters(method=cls.__name__)

Wagner ¤

Wagner equation for vapor pressure.

This equation implements the following temperature dependence:

\[ \ln(P^*/P_c) = \frac{a\tau + b\tau^{1.5} + c\tau^{2.5} + d\tau^5}{T_r}\]

with:

\[ \tau = 1 - T_r\]

where \(a\) to \(d\) are component-specific constants, \(P^*\) is the vapor pressure, \(P_c\) is the critical pressure, \(T\) is the absolute temperature, \(T_c\) is the critical temperature, and \(T_r=T/T_c\) is the reduced temperature.

Note

There are several versions of the Wagner equation with different exponents. This is the so-called 25 version also used in the ThermoData Engine.

PARAMETER DESCRIPTION
Tc

Critical temperature. Unit = K.

TYPE: float

Pc

Critical pressure. Unit = Any.

TYPE: float

a

Parameter of equation.

TYPE: float

b

Parameter of equation.

TYPE: float

c

Parameter of equation.

TYPE: float

d

Parameter of equation.

TYPE: float

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

unit

Unit of vapor pressure.

TYPE: str DEFAULT: 'Pa'

symbol

Symbol of vapor pressure.

TYPE: str DEFAULT: 'P^*'

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/equations/vapor_pressure.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
class Wagner(PropertyEquationT):
    r"""[Wagner](https://de.wikipedia.org/wiki/Wagner-Gleichung) equation for
    vapor pressure.

    This equation implements the following temperature dependence:

    $$ \ln(P^*/P_c) = \frac{a\tau + b\tau^{1.5} + c\tau^{2.5} + d\tau^5}{T_r}$$

    with:

    $$ \tau = 1 - T_r$$

    where $a$ to $d$ are component-specific constants, $P^*$ is the vapor
    pressure, $P_c$ is the critical pressure, $T$ is the absolute temperature,
    $T_c$ is the critical temperature, and $T_r=T/T_c$ is the reduced
    temperature.

    !!! note

        There are several versions of the Wagner equation with different
        exponents. This is the so-called 25 version also used in the
        [ThermoData Engine](https://trc.nist.gov/tde.html).

    Parameters
    ----------
    Tc : float
        Critical temperature.
        Unit = K.
    Pc : float
        Critical pressure.
        Unit = Any.
    a : float
        Parameter of equation.
    b : float
        Parameter of equation.
    c : float
        Parameter of equation.
    d : float
        Parameter of equation.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    unit : str
        Unit of vapor pressure.
    symbol : str
        Symbol of vapor pressure.
    name : str
        Name.
    """

    _pinfo = {'a': ('', True), 'b': ('', True), 'c': ('', True),
              'd': ('', True), 'Pc': ('#', False), 'Tc': ('K', False)}

    def __init__(self,
                 a: float,
                 b: float,
                 c: float,
                 d: float,
                 Pc: float,
                 Tc: float,
                 Tmin: float = 0.0,
                 Tmax: float = np.inf,
                 unit: str = 'Pa',
                 symbol: str = 'P^*',
                 name: str = ''
                 ) -> None:

        self.p = {'a': a, 'b': b, 'c': c, 'd': d, 'Pc': Pc, 'Tc': Tc}
        super().__init__((Tmin, Tmax), unit, symbol, name)

    @staticmethod
    def equation(T: Union[float, FloatArray],
                 a: float,
                 b: float,
                 c: float,
                 d: float,
                 Pc: float,
                 Tc: float,
                 ) -> Union[float, FloatArray]:
        r"""Wagner equation.

        Parameters
        ----------
        T : float | FloatArray
            Temperature. Unit = K.
        a : float
            Parameter of equation.
        b : float
            Parameter of equation.
        c : float
            Parameter of equation.
        d : float
            Parameter of equation.
        Pc : float
            Critical pressure.
            Unit = Any.
        Tc : float
            Critical temperature.
            Unit = K.

        Returns
        -------
        float | FloatArray
            Vapor pressure. Unit = [Pc].
        """
        Tr = T/Tc
        t = 1 - Tr
        return Pc*exp((a*t + b*t**1.5 + c*t**2.5 + d*t**5)/Tr)

__call__ ¤

__call__(
    T: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
) -> Union[float, FloatArray]

Evaluate property equation at given temperature, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

RETURNS DESCRIPTION
float | FloatArray

Correlation value.

Source code in src/polykin/properties/equations/base.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __call__(self,
             T: Union[float, FloatArrayLike],
             Tunit: Literal['C', 'K'] = 'K'
             ) -> Union[float, FloatArray]:
    r"""Evaluate property equation at given temperature, including unit
    conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    Tunit : Literal['C', 'K']
        Temperature unit.

    Returns
    -------
    float | FloatArray
        Correlation value.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    return self.equation(TK, **self.p)

equation staticmethod ¤

equation(
    T: Union[float, FloatArray],
    a: float,
    b: float,
    c: float,
    d: float,
    Pc: float,
    Tc: float,
) -> Union[float, FloatArray]

Wagner equation.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

a

Parameter of equation.

TYPE: float

b

Parameter of equation.

TYPE: float

c

Parameter of equation.

TYPE: float

d

Parameter of equation.

TYPE: float

Pc

Critical pressure. Unit = Any.

TYPE: float

Tc

Critical temperature. Unit = K.

TYPE: float

RETURNS DESCRIPTION
float | FloatArray

Vapor pressure. Unit = [Pc].

Source code in src/polykin/properties/equations/vapor_pressure.py
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
@staticmethod
def equation(T: Union[float, FloatArray],
             a: float,
             b: float,
             c: float,
             d: float,
             Pc: float,
             Tc: float,
             ) -> Union[float, FloatArray]:
    r"""Wagner equation.

    Parameters
    ----------
    T : float | FloatArray
        Temperature. Unit = K.
    a : float
        Parameter of equation.
    b : float
        Parameter of equation.
    c : float
        Parameter of equation.
    d : float
        Parameter of equation.
    Pc : float
        Critical pressure.
        Unit = Any.
    Tc : float
        Critical temperature.
        Unit = K.

    Returns
    -------
    float | FloatArray
        Vapor pressure. Unit = [Pc].
    """
    Tr = T/Tc
    t = 1 - Tr
    return Pc*exp((a*t + b*t**1.5 + c*t**2.5 + d*t**5)/Tr)

fit ¤

fit(
    T: FloatVectorLike,
    Y: FloatVectorLike,
    sigmaY: Optional[FloatVectorLike] = None,
    fit_only: Optional[list[str]] = None,
    logY: bool = False,
    plot: bool = True,
) -> dict

Fit equation to data using non-linear regression.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: FloatVector

Y

Property to be fitted. Unit = Any.

TYPE: FloatVector

sigmaY

Standard deviation of Y. Unit = [Y].

TYPE: FloatVector | None DEFAULT: None

fit_only

List with name of parameters to be fitted.

TYPE: list[str] | None DEFAULT: None

logY

If True, the fit will be done in terms of log(Y).

TYPE: bool DEFAULT: False

plot

If True a plot comparing data and fitted correlation will be generated.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
dict

A dictionary of results with the following keys: 'success', 'parameters', 'covariance', and 'plot'.

Source code in src/polykin/properties/equations/base.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def fit(self,
        T: FloatVectorLike,
        Y: FloatVectorLike,
        sigmaY: Optional[FloatVectorLike] = None,
        fit_only: Optional[list[str]] = None,
        logY: bool = False,
        plot: bool = True,
        ) -> dict:
    """Fit equation to data using non-linear regression.

    Parameters
    ----------
    T : FloatVector
        Temperature. Unit = K.
    Y : FloatVector
        Property to be fitted. Unit = Any.
    sigmaY : FloatVector | None
        Standard deviation of Y. Unit = [Y].
    fit_only : list[str] | None
        List with name of parameters to be fitted.
    logY : bool
        If `True`, the fit will be done in terms of log(Y).
    plot : bool
        If `True` a plot comparing data and fitted correlation will be
        generated.

    Returns
    -------
    dict
        A dictionary of results with the following keys: 'success',
        'parameters', 'covariance', and 'plot'.
    """

    # Current parameter values
    pdict = self.p.copy()

    # Select parameters to be fitted
    pnames_fit = [name for name, info in self._pinfo.items() if info[1]]
    if fit_only:
        pnames_fit = set(fit_only) & set(pnames_fit)
    p0 = [pdict[pname] for pname in pnames_fit]

    # Fit function
    def ffit(x, *p):
        for pname, pvalue in zip(pnames_fit, p):
            pdict[pname] = pvalue
        Yfit = self.equation(T=x, **pdict)
        if logY:
            Yfit = log(Yfit)
        return Yfit

    solution = curve_fit(ffit,
                         xdata=T,
                         ydata=log(Y) if logY else Y,
                         p0=p0,
                         sigma=sigmaY,
                         absolute_sigma=False,
                         full_output=True)
    result = {}
    result['success'] = bool(solution[4])
    if solution[4]:
        popt = solution[0]
        pcov = solution[1]
        print("Fit successful.")
        for pname, pvalue in zip(pnames_fit, popt):
            print(f"{pname}: {pvalue}")
        print("Covariance:")
        print(pcov)
        result['covariance'] = pcov

        # Update attributes
        self.Trange = (min(T), max(T))
        for pname, pvalue in zip(pnames_fit, popt):
            self.p[pname] = pvalue
        result['parameters'] = pdict

        # plot
        if plot:
            kind = 'semilogy' if logY else 'linear'
            fig, ax = self.plot(kind=kind, return_objects=True)  # ok
            ax.plot(T, Y, 'o', mfc='none')
            result['plot'] = (fig, ax)
    else:
        print("Fit error: ", solution[3])
        result['message'] = solution[3]

    return result

plot ¤

plot(
    kind: Literal[
        "linear", "semilogy", "Arrhenius"
    ] = "linear",
    Trange: Optional[tuple[float, float]] = None,
    Tunit: Literal["C", "K"] = "K",
    title: Optional[str] = None,
    axes: Optional[Axes] = None,
    return_objects: bool = False,
) -> Optional[tuple[Optional[Figure], Axes]]

Plot quantity as a function of temperature.

PARAMETER DESCRIPTION
kind

Kind of plot to be generated.

TYPE: Literal['linear', 'semilogy', 'Arrhenius'] DEFAULT: 'linear'

Trange

Temperature range for x-axis. If None, the validity range (Tmin, Tmax) will be used. If no validity range was defined, the range will default to 0-100°C.

TYPE: tuple[float, float] | None DEFAULT: None

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

title

Title of plot. If None, the object name will be used.

TYPE: str | None DEFAULT: None

axes

Matplotlib Axes object.

TYPE: Axes | None DEFAULT: None

return_objects

If True, the Figure and Axes objects are returned (for saving or further manipulations).

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
tuple[Figure | None, Axes] | None

Figure and Axes objects if return_objects is True.

Source code in src/polykin/properties/equations/base.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def plot(self,
         kind: Literal['linear', 'semilogy', 'Arrhenius'] = 'linear',
         Trange: Optional[tuple[float, float]] = None,
         Tunit: Literal['C', 'K'] = 'K',
         title: Optional[str] = None,
         axes: Optional[Axes] = None,
         return_objects: bool = False
         ) -> Optional[tuple[Optional[Figure], Axes]]:
    """Plot quantity as a function of temperature.

    Parameters
    ----------
    kind : Literal['linear', 'semilogy', 'Arrhenius']
        Kind of plot to be generated.
    Trange : tuple[float, float] | None
        Temperature range for x-axis. If `None`, the validity range
        (Tmin, Tmax) will be used. If no validity range was defined, the
        range will default to 0-100°C.
    Tunit : Literal['C', 'K']
        Temperature unit.
    title : str | None
        Title of plot. If `None`, the object name will be used.
    axes : Axes | None
        Matplotlib Axes object.
    return_objects : bool
        If `True`, the Figure and Axes objects are returned (for saving or
        further manipulations).

    Returns
    -------
    tuple[Figure | None, Axes] | None
        Figure and Axes objects if return_objects is `True`.
    """

    # Check inputs
    check_in_set(kind, {'linear', 'semilogy', 'Arrhenius'}, 'kind')
    check_in_set(Tunit, {'K', 'C'}, 'Tunit')
    if Trange is not None:
        Trange_min = 0.
        if Tunit == 'C':
            Trange_min = -273.15
        check_valid_range(Trange, Trange_min, np.inf, 'Trange')

    # Plot objects
    if axes is None:
        fig, ax = plt.subplots()
        if title is None:
            title = self.name
        if title:
            fig.suptitle(title)
        label = None
    else:
        fig = None
        ax = axes
        label = self.name

    # Units and xlabel
    Tunit_range = Tunit
    if kind == 'Arrhenius':
        Tunit = 'K'
    Tsymbol = Tunit
    if Tunit == 'C':
        Tsymbol = '°' + Tunit

    if kind == 'Arrhenius':
        xlabel = r"$1/T$ [" + Tsymbol + r"$^{-1}$]"
    else:
        xlabel = fr"$T$ [{Tsymbol}]"

    # ylabel
    ylabel = fr"${self.symbol}$ [{self.unit}]"
    if axes is not None:
        ylabel0 = ax.get_ylabel()
        if ylabel0 and ylabel not in ylabel0:
            ylabel = ylabel0 + ", " + ylabel

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True)

    # x-axis vector
    if Trange is not None:
        if Tunit_range == 'C':
            Trange = (Trange[0]+273.15, Trange[1]+273.15)
    else:
        Trange = (np.min(self.Trange[0]), np.max(self.Trange[1]))
        if Trange == (0.0, np.inf):
            Trange = (273.15, 373.15)

    try:
        shape = self._shape
    except AttributeError:
        shape = None
    if shape is not None:
        print("Plot method not yet implemented for array-like equations.")
    else:
        TK = np.linspace(*Trange, 100)
        y = self.__call__(TK, 'K')
        T = TK
        if Tunit == 'C':
            T -= 273.15
        if kind == 'linear':
            ax.plot(T, y, label=label)
        elif kind == 'semilogy':
            ax.semilogy(T, y, label=label)
        elif kind == 'Arrhenius':
            ax.semilogy(1/TK, y, label=label)

    if fig is None:
        ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")

    if return_objects:
        return (fig, ax)

Yaws ¤

Yaws equation for saturated liquid viscosity.

This equation implements the following temperature dependence:

\[ \log_{base} \mu = A + \frac{B}{T} + C T + D T^2 \]

where \(A\) to \(D\) are component-specific constants, \(\mu\) is the liquid viscosity, and \(T\) is the temperature. When \(C=D=0\), this equation reverts to the Andrade equation.

PARAMETER DESCRIPTION
A

Parameter of equation.

TYPE: float

B

Parameter of equation. Unit = K.

TYPE: float

C

Parameter of equation. Unit = K⁻¹.

TYPE: float DEFAULT: 0.0

D

Parameter of equation. Unit = K⁻².

TYPE: float DEFAULT: 0.0

base10

If True base of logarithm is 10, otherwise it is \(e\).

TYPE: bool DEFAULT: True

Tmin

Lower temperature bound. Unit = K.

TYPE: float DEFAULT: 0.0

Tmax

Upper temperature bound. Unit = K.

TYPE: float DEFAULT: inf

unit

Unit of viscosity.

TYPE: str DEFAULT: 'Pa·s'

symbol

Symbol of viscosity.

TYPE: str DEFAULT: '\\mu'

name

Name.

TYPE: str DEFAULT: ''

Source code in src/polykin/properties/equations/viscosity.py
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
class Yaws(PropertyEquationT):
    r"""Yaws equation for saturated liquid viscosity.

    This equation implements the following temperature dependence:

    $$ \log_{base} \mu = A + \frac{B}{T} + C T + D T^2 $$

    where $A$ to $D$ are component-specific constants, $\mu$ is the liquid
    viscosity, and $T$ is the temperature. When $C=D=0$, this equation reverts
    to the Andrade equation.

    Parameters
    ----------
    A : float
        Parameter of equation.
    B : float
        Parameter of equation.
        Unit = K.
    C : float
        Parameter of equation.
        Unit = K⁻¹.
    D : float
        Parameter of equation.
        Unit = K⁻².
    base10 : bool
        If `True` base of logarithm is `10`, otherwise it is $e$.
    Tmin : float
        Lower temperature bound.
        Unit = K.
    Tmax : float
        Upper temperature bound.
        Unit = K.
    unit : str
        Unit of viscosity.
    symbol : str
        Symbol of viscosity.
    name : str
        Name.
    """

    _pinfo = {'A': ('', True), 'B': ('K', True), 'C': ('K⁻¹', True),
              'D': ('K⁻²', True), 'base10': ('', False)}

    def __init__(self,
                 A: float,
                 B: float,
                 C: float = 0.,
                 D: float = 0.,
                 base10: bool = True,
                 Tmin: float = 0.,
                 Tmax: float = np.inf,
                 unit: str = 'Pa·s',
                 symbol: str = r'\mu',
                 name: str = ''
                 ) -> None:
        """Construct `Yaws` with the given parameters."""

        self.p = {'A': A, 'B': B, 'C': C, 'D': D, 'base10': base10}
        super().__init__((Tmin, Tmax), unit, symbol, name)

    @staticmethod
    def equation(T: Union[float, FloatArray],
                 A: float,
                 B: float,
                 C: float,
                 D: float,
                 base10: bool
                 ) -> Union[float, FloatArray]:
        r"""Yaws equation.

        Parameters
        ----------
        T : float | FloatArray
            Temperature. Unit = K.
        A : float
            Parameter of equation.
        B : float
            Parameter of equation.
            Unit = K.
        C : float
            Parameter of equation.
            Unit = K⁻¹.
        D : float
            Parameter of equation.
            Unit = K⁻².
        base10 : bool
            If `True` base of logarithm is `10`, otherwise it is $e$.

        Returns
        -------
        float | FloatArray
            Viscosity. Unit = Any.
        """
        x = A + B/T + C*T + D*T**2
        if base10:
            return 10**x
        else:
            return exp(x)

__call__ ¤

__call__(
    T: Union[float, FloatArrayLike],
    Tunit: Literal["C", "K"] = "K",
) -> Union[float, FloatArray]

Evaluate property equation at given temperature, including unit conversion and range check.

PARAMETER DESCRIPTION
T

Temperature. Unit = Tunit.

TYPE: float | FloatArrayLike

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

RETURNS DESCRIPTION
float | FloatArray

Correlation value.

Source code in src/polykin/properties/equations/base.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __call__(self,
             T: Union[float, FloatArrayLike],
             Tunit: Literal['C', 'K'] = 'K'
             ) -> Union[float, FloatArray]:
    r"""Evaluate property equation at given temperature, including unit
    conversion and range check.

    Parameters
    ----------
    T : float | FloatArrayLike
        Temperature.
        Unit = `Tunit`.
    Tunit : Literal['C', 'K']
        Temperature unit.

    Returns
    -------
    float | FloatArray
        Correlation value.
    """
    TK = convert_check_temperature(T, Tunit, self.Trange)
    return self.equation(TK, **self.p)

__init__ ¤

__init__(
    A: float,
    B: float,
    C: float = 0.0,
    D: float = 0.0,
    base10: bool = True,
    Tmin: float = 0.0,
    Tmax: float = np.inf,
    unit: str = "Pa·s",
    symbol: str = "\\mu",
    name: str = "",
) -> None

Construct Yaws with the given parameters.

Source code in src/polykin/properties/equations/viscosity.py
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def __init__(self,
             A: float,
             B: float,
             C: float = 0.,
             D: float = 0.,
             base10: bool = True,
             Tmin: float = 0.,
             Tmax: float = np.inf,
             unit: str = 'Pa·s',
             symbol: str = r'\mu',
             name: str = ''
             ) -> None:
    """Construct `Yaws` with the given parameters."""

    self.p = {'A': A, 'B': B, 'C': C, 'D': D, 'base10': base10}
    super().__init__((Tmin, Tmax), unit, symbol, name)

equation staticmethod ¤

equation(
    T: Union[float, FloatArray],
    A: float,
    B: float,
    C: float,
    D: float,
    base10: bool,
) -> Union[float, FloatArray]

Yaws equation.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: float | FloatArray

A

Parameter of equation.

TYPE: float

B

Parameter of equation. Unit = K.

TYPE: float

C

Parameter of equation. Unit = K⁻¹.

TYPE: float

D

Parameter of equation. Unit = K⁻².

TYPE: float

base10

If True base of logarithm is 10, otherwise it is \(e\).

TYPE: bool

RETURNS DESCRIPTION
float | FloatArray

Viscosity. Unit = Any.

Source code in src/polykin/properties/equations/viscosity.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@staticmethod
def equation(T: Union[float, FloatArray],
             A: float,
             B: float,
             C: float,
             D: float,
             base10: bool
             ) -> Union[float, FloatArray]:
    r"""Yaws equation.

    Parameters
    ----------
    T : float | FloatArray
        Temperature. Unit = K.
    A : float
        Parameter of equation.
    B : float
        Parameter of equation.
        Unit = K.
    C : float
        Parameter of equation.
        Unit = K⁻¹.
    D : float
        Parameter of equation.
        Unit = K⁻².
    base10 : bool
        If `True` base of logarithm is `10`, otherwise it is $e$.

    Returns
    -------
    float | FloatArray
        Viscosity. Unit = Any.
    """
    x = A + B/T + C*T + D*T**2
    if base10:
        return 10**x
    else:
        return exp(x)

fit ¤

fit(
    T: FloatVectorLike,
    Y: FloatVectorLike,
    sigmaY: Optional[FloatVectorLike] = None,
    fit_only: Optional[list[str]] = None,
    logY: bool = False,
    plot: bool = True,
) -> dict

Fit equation to data using non-linear regression.

PARAMETER DESCRIPTION
T

Temperature. Unit = K.

TYPE: FloatVector

Y

Property to be fitted. Unit = Any.

TYPE: FloatVector

sigmaY

Standard deviation of Y. Unit = [Y].

TYPE: FloatVector | None DEFAULT: None

fit_only

List with name of parameters to be fitted.

TYPE: list[str] | None DEFAULT: None

logY

If True, the fit will be done in terms of log(Y).

TYPE: bool DEFAULT: False

plot

If True a plot comparing data and fitted correlation will be generated.

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
dict

A dictionary of results with the following keys: 'success', 'parameters', 'covariance', and 'plot'.

Source code in src/polykin/properties/equations/base.py
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
def fit(self,
        T: FloatVectorLike,
        Y: FloatVectorLike,
        sigmaY: Optional[FloatVectorLike] = None,
        fit_only: Optional[list[str]] = None,
        logY: bool = False,
        plot: bool = True,
        ) -> dict:
    """Fit equation to data using non-linear regression.

    Parameters
    ----------
    T : FloatVector
        Temperature. Unit = K.
    Y : FloatVector
        Property to be fitted. Unit = Any.
    sigmaY : FloatVector | None
        Standard deviation of Y. Unit = [Y].
    fit_only : list[str] | None
        List with name of parameters to be fitted.
    logY : bool
        If `True`, the fit will be done in terms of log(Y).
    plot : bool
        If `True` a plot comparing data and fitted correlation will be
        generated.

    Returns
    -------
    dict
        A dictionary of results with the following keys: 'success',
        'parameters', 'covariance', and 'plot'.
    """

    # Current parameter values
    pdict = self.p.copy()

    # Select parameters to be fitted
    pnames_fit = [name for name, info in self._pinfo.items() if info[1]]
    if fit_only:
        pnames_fit = set(fit_only) & set(pnames_fit)
    p0 = [pdict[pname] for pname in pnames_fit]

    # Fit function
    def ffit(x, *p):
        for pname, pvalue in zip(pnames_fit, p):
            pdict[pname] = pvalue
        Yfit = self.equation(T=x, **pdict)
        if logY:
            Yfit = log(Yfit)
        return Yfit

    solution = curve_fit(ffit,
                         xdata=T,
                         ydata=log(Y) if logY else Y,
                         p0=p0,
                         sigma=sigmaY,
                         absolute_sigma=False,
                         full_output=True)
    result = {}
    result['success'] = bool(solution[4])
    if solution[4]:
        popt = solution[0]
        pcov = solution[1]
        print("Fit successful.")
        for pname, pvalue in zip(pnames_fit, popt):
            print(f"{pname}: {pvalue}")
        print("Covariance:")
        print(pcov)
        result['covariance'] = pcov

        # Update attributes
        self.Trange = (min(T), max(T))
        for pname, pvalue in zip(pnames_fit, popt):
            self.p[pname] = pvalue
        result['parameters'] = pdict

        # plot
        if plot:
            kind = 'semilogy' if logY else 'linear'
            fig, ax = self.plot(kind=kind, return_objects=True)  # ok
            ax.plot(T, Y, 'o', mfc='none')
            result['plot'] = (fig, ax)
    else:
        print("Fit error: ", solution[3])
        result['message'] = solution[3]

    return result

plot ¤

plot(
    kind: Literal[
        "linear", "semilogy", "Arrhenius"
    ] = "linear",
    Trange: Optional[tuple[float, float]] = None,
    Tunit: Literal["C", "K"] = "K",
    title: Optional[str] = None,
    axes: Optional[Axes] = None,
    return_objects: bool = False,
) -> Optional[tuple[Optional[Figure], Axes]]

Plot quantity as a function of temperature.

PARAMETER DESCRIPTION
kind

Kind of plot to be generated.

TYPE: Literal['linear', 'semilogy', 'Arrhenius'] DEFAULT: 'linear'

Trange

Temperature range for x-axis. If None, the validity range (Tmin, Tmax) will be used. If no validity range was defined, the range will default to 0-100°C.

TYPE: tuple[float, float] | None DEFAULT: None

Tunit

Temperature unit.

TYPE: Literal['C', 'K'] DEFAULT: 'K'

title

Title of plot. If None, the object name will be used.

TYPE: str | None DEFAULT: None

axes

Matplotlib Axes object.

TYPE: Axes | None DEFAULT: None

return_objects

If True, the Figure and Axes objects are returned (for saving or further manipulations).

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
tuple[Figure | None, Axes] | None

Figure and Axes objects if return_objects is True.

Source code in src/polykin/properties/equations/base.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
def plot(self,
         kind: Literal['linear', 'semilogy', 'Arrhenius'] = 'linear',
         Trange: Optional[tuple[float, float]] = None,
         Tunit: Literal['C', 'K'] = 'K',
         title: Optional[str] = None,
         axes: Optional[Axes] = None,
         return_objects: bool = False
         ) -> Optional[tuple[Optional[Figure], Axes]]:
    """Plot quantity as a function of temperature.

    Parameters
    ----------
    kind : Literal['linear', 'semilogy', 'Arrhenius']
        Kind of plot to be generated.
    Trange : tuple[float, float] | None
        Temperature range for x-axis. If `None`, the validity range
        (Tmin, Tmax) will be used. If no validity range was defined, the
        range will default to 0-100°C.
    Tunit : Literal['C', 'K']
        Temperature unit.
    title : str | None
        Title of plot. If `None`, the object name will be used.
    axes : Axes | None
        Matplotlib Axes object.
    return_objects : bool
        If `True`, the Figure and Axes objects are returned (for saving or
        further manipulations).

    Returns
    -------
    tuple[Figure | None, Axes] | None
        Figure and Axes objects if return_objects is `True`.
    """

    # Check inputs
    check_in_set(kind, {'linear', 'semilogy', 'Arrhenius'}, 'kind')
    check_in_set(Tunit, {'K', 'C'}, 'Tunit')
    if Trange is not None:
        Trange_min = 0.
        if Tunit == 'C':
            Trange_min = -273.15
        check_valid_range(Trange, Trange_min, np.inf, 'Trange')

    # Plot objects
    if axes is None:
        fig, ax = plt.subplots()
        if title is None:
            title = self.name
        if title:
            fig.suptitle(title)
        label = None
    else:
        fig = None
        ax = axes
        label = self.name

    # Units and xlabel
    Tunit_range = Tunit
    if kind == 'Arrhenius':
        Tunit = 'K'
    Tsymbol = Tunit
    if Tunit == 'C':
        Tsymbol = '°' + Tunit

    if kind == 'Arrhenius':
        xlabel = r"$1/T$ [" + Tsymbol + r"$^{-1}$]"
    else:
        xlabel = fr"$T$ [{Tsymbol}]"

    # ylabel
    ylabel = fr"${self.symbol}$ [{self.unit}]"
    if axes is not None:
        ylabel0 = ax.get_ylabel()
        if ylabel0 and ylabel not in ylabel0:
            ylabel = ylabel0 + ", " + ylabel

    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True)

    # x-axis vector
    if Trange is not None:
        if Tunit_range == 'C':
            Trange = (Trange[0]+273.15, Trange[1]+273.15)
    else:
        Trange = (np.min(self.Trange[0]), np.max(self.Trange[1]))
        if Trange == (0.0, np.inf):
            Trange = (273.15, 373.15)

    try:
        shape = self._shape
    except AttributeError:
        shape = None
    if shape is not None:
        print("Plot method not yet implemented for array-like equations.")
    else:
        TK = np.linspace(*Trange, 100)
        y = self.__call__(TK, 'K')
        T = TK
        if Tunit == 'C':
            T -= 273.15
        if kind == 'linear':
            ax.plot(T, y, label=label)
        elif kind == 'semilogy':
            ax.semilogy(T, y, label=label)
        elif kind == 'Arrhenius':
            ax.semilogy(1/TK, y, label=label)

    if fig is None:
        ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")

    if return_objects:
        return (fig, ax)

geometric_interaction_mixing ¤

geometric_interaction_mixing(
    y: FloatVector,
    Q: FloatVector,
    k: FloatSquareMatrix | None = None,
) -> float

Calculate a mixture parameter using a geometric average with interaction.

\[ Q_m = \sum_i \sum_j y_i y_j (Q_i Q_j)^{1/2}(1-k_{ij}) \]

with \(k_{ii}=0\) and \(k_{i,j}=k_{j,i}\).

Note

Only the entries above the main diagonal of \(k_{i,j}\) are used.

References

  • RC Reid, JM Prausniz, and BE Poling. The properties of gases & liquids 4th edition, 1986, p. 75.
PARAMETER DESCRIPTION
y

Composition, usually molar or mass fractions.

TYPE: FloatVector

Q

Pure component property.

TYPE: FloatVector

k

Binary interaction parameter matrix.

TYPE: FloatSquareMatrix | None DEFAULT: None

RETURNS DESCRIPTION
float

Mixture parameter, \(Q_m\). Unit = [Q].

Source code in src/polykin/properties/mixing_rules.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def geometric_interaction_mixing(y: FloatVector,
                                 Q: FloatVector,
                                 k: FloatSquareMatrix | None = None
                                 ) -> float:
    r"""Calculate a mixture parameter using a geometric average with
    interaction.

    $$ Q_m = \sum_i \sum_j y_i y_j (Q_i Q_j)^{1/2}(1-k_{ij}) $$

    with $k_{ii}=0$ and $k_{i,j}=k_{j,i}$.

    !!! note

        Only the entries above the main diagonal of $k_{i,j}$ are used.

    **References**

    *   RC Reid, JM Prausniz, and BE Poling. The properties of gases & liquids
        4th edition, 1986, p. 75.

    Parameters
    ----------
    y : FloatVector
        Composition, usually molar or mass fractions.
    Q : FloatVector
        Pure component property.
    k : FloatSquareMatrix | None
        Binary interaction parameter matrix.

    Returns
    -------
    float
        Mixture parameter, $Q_m$. Unit = [Q].
    """
    if k is None:
        Qm = (dot(y, sqrt(Q)))**2
    else:
        N = y.size
        Qm = 0.
        for i in range(N):
            Qm += y[i]**2 * Q[i]
            for j in range(i+1, N):
                Qm += 2*y[i]*y[j]*sqrt(Q[i]*Q[j])*(1. - k[i, j])
    return Qm

plotequations ¤

plotequations(
    eqs: list[PropertyEquationT],
    kind: Literal[
        "linear", "semilogy", "Arrhenius"
    ] = "linear",
    title: Optional[str] = None,
    **kwargs
) -> Figure

Plot a list of temperature-dependent property equations in a combined plot.

PARAMETER DESCRIPTION
eqs

List of property equations to be ploted together.

TYPE: list[PropertyEquationT]

kind

Kind of plot to be generated.

TYPE: Literal['linear', 'semilogy', 'Arrhenius'] DEFAULT: 'linear'

title

Title of plot.

TYPE: str | None DEFAULT: None

RETURNS DESCRIPTION
Figure

Matplotlib Figure object holding the combined plot.

Source code in src/polykin/properties/equations/base.py
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
def plotequations(eqs: list[PropertyEquationT],
                  kind: Literal['linear', 'semilogy', 'Arrhenius'] = 'linear',
                  title: Optional[str] = None,
                  **kwargs
                  ) -> Figure:
    """Plot a list of temperature-dependent property equations in a combined
    plot.

    Parameters
    ----------
    eqs : list[PropertyEquationT]
        List of property equations to be ploted together.
    kind : Literal['linear', 'semilogy', 'Arrhenius']
        Kind of plot to be generated.
    title : str | None
        Title of plot.

    Returns
    -------
    Figure
        Matplotlib Figure object holding the combined plot.
    """

    # Create matplotlib objects
    fig, ax = plt.subplots()

    # Title
    if title is None:
        title = "Equation overlay"
    fig.suptitle(title)

    # Draw plots sequentially
    for eq in eqs:
        eq.plot(kind=kind, axes=ax, **kwargs)

    return fig

pseudocritical_properties ¤

pseudocritical_properties(
    y: FloatVector,
    Tc: FloatVector,
    Pc: FloatVector,
    Zc: FloatVector,
    w: FloatVector | None = None,
) -> tuple[float, float, float, float, float]

Calculate the pseudocritial properties of a mixture to use in corresponding states correlations.

\[ \begin{aligned} T_{cm} &= \sum_i y_i T_{ci} \\ Z_{cm} &= \sum_i y_i Z_{ci} \\ v_{cm} &= \sum_i y_i \frac{Z_{ci} R T_{ci}}{P_{ci}} \\ P_{cm} &= \frac{Z_{cm} R T_{cm}}{v_{cm}} \\ \omega_{cm} &= \sum_i y_i \omega_{ci} \end{aligned} \]

where the meaning of the parameters is as defined below.

References

  • RC Reid, JM Prausniz, and BE Poling. The properties of gases & liquids 4th edition, 1986, p. 76-77.
PARAMETER DESCRIPTION
y

Mole fractions of all components. Unit = mol/mol.

TYPE: FloatVector

Tc

Critical temperatures of all components. Unit = K.

TYPE: FloatVector

Pc

Critical pressures of all components. Unit = Pa.

TYPE: FloatVector

Zc

Critical compressibility factors of all components.

TYPE: FloatVector

w

Acentric factors of all components.

TYPE: FloatVector | None DEFAULT: None

RETURNS DESCRIPTION
tuple[float, float, float, float, float]

Tuple of pseudocritial properties, \((T_{cm}, P_{cm}, v_{cm}, Z_{cm}, \omega_{cm})\).

Source code in src/polykin/properties/mixing_rules.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def pseudocritical_properties(y: FloatVector,
                              Tc: FloatVector,
                              Pc: FloatVector,
                              Zc: FloatVector,
                              w: FloatVector | None = None
                              ) -> tuple[float, float, float, float, float]:
    r"""Calculate the pseudocritial properties of a mixture to use in
    corresponding states correlations.

    $$ \begin{aligned}
        T_{cm} &= \sum_i y_i T_{ci} \\
        Z_{cm} &= \sum_i y_i Z_{ci} \\
        v_{cm} &= \sum_i y_i \frac{Z_{ci} R T_{ci}}{P_{ci}} \\
        P_{cm} &= \frac{Z_{cm} R T_{cm}}{v_{cm}} \\
        \omega_{cm} &= \sum_i y_i \omega_{ci}
    \end{aligned} $$

    where the meaning of the parameters is as defined below.

    **References**

    *   RC Reid, JM Prausniz, and BE Poling. The properties of gases & liquids
        4th edition, 1986, p. 76-77.

    Parameters
    ----------
    y : FloatVector
        Mole fractions of all components. Unit = mol/mol.
    Tc : FloatVector
        Critical temperatures of all components. Unit = K.
    Pc : FloatVector
        Critical pressures of all components. Unit = Pa.
    Zc : FloatVector
        Critical compressibility factors of all components.
    w : FloatVector | None
        Acentric factors of all components.

    Returns
    -------
    tuple[float, float, float, float, float]
        Tuple of pseudocritial properties,
        $(T_{cm}, P_{cm}, v_{cm}, Z_{cm}, \omega_{cm})$.
    """

    Tc_mix = dot(y, Tc)
    Zc_mix = dot(y, Zc)
    vc = Zc*R*Tc/Pc
    vc_mix = dot(y, vc)
    Pc_mix = R*Zc_mix*Tc_mix/vc_mix

    if w is not None:
        w_mix = dot(y, w)
    else:
        w_mix = -1e99

    return (Tc_mix, Pc_mix, vc_mix, Zc_mix, w_mix)

quadratic_mixing ¤

quadratic_mixing(
    y: FloatVector, Q: FloatSquareMatrix
) -> float

Calculate a mixture parameter using a quadratic mixing rule.

\[ Q_m = \sum_i \sum_j y_i y_j Q_{ij} \]

Note

For the sake of generality, no assumptions are made regarding the symmetry of \(Q_{ij}\). The full matrix must be supplied and will be used as given.

References

  • RC Reid, JM Prausniz, and BE Poling. The properties of gases & liquids 4th edition, 1986, p. 75.
PARAMETER DESCRIPTION
y

Composition, usually molar or mass fractions.

TYPE: FloatVector

Q

Matrix of pure component and interaction parameters.

TYPE: FloatSquareMatrix

RETURNS DESCRIPTION
float

Mixture parameter, \(Q_m\). Unit = [Q].

Source code in src/polykin/properties/mixing_rules.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def quadratic_mixing(y: FloatVector,
                     Q: FloatSquareMatrix
                     ) -> float:
    r"""Calculate a mixture parameter using a quadratic mixing rule.

    $$ Q_m = \sum_i \sum_j y_i y_j Q_{ij} $$

    !!! note

        For the sake of generality, no assumptions are made regarding the
        symmetry of $Q_{ij}$. The _full_ matrix must be supplied and will be
        used as given.

    **References**

    *   RC Reid, JM Prausniz, and BE Poling. The properties of gases & liquids
        4th edition, 1986, p. 75.

    Parameters
    ----------
    y : FloatVector
        Composition, usually molar or mass fractions.
    Q : FloatSquareMatrix
        Matrix of pure component and interaction parameters.

    Returns
    -------
    float
        Mixture parameter, $Q_m$. Unit = [Q].
    """
    return dot(y, dot(y, Q))