rfluids 0.3.1

🦀 Rusty CoolProp wrapper
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
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
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
309
310
311
312
313
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
381
382
383
384
385
386
387
388
389
390
391
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
464
465
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
use std::ffi::CString;

use coolprop_sys::COOLPROP;

use super::{
    CoolProp, Result,
    common::{StringBuffer, get_error},
};
use crate::io::ConfigValue;

const MIN_DEBUG_LEVEL: u8 = 0;
const MAX_DEBUG_LEVEL: u8 = 10;

impl CoolProp {
    /// Returns the current `CoolProp` debug level.
    ///
    /// The debug level controls the verbosity of debugging output from `CoolProp`.
    /// Valid range is `0-10`:
    ///
    /// - `0` -- debugging output is disabled
    /// - `> 0` -- debugging output is enabled, with larger values producing more verbose output
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use rfluids::prelude::*;
    ///
    /// // By default, debugging output is disabled
    /// let debug_level = CoolProp::get_debug_level();
    /// assert_eq!(debug_level, 0);
    /// ```
    ///
    /// # See Also
    ///
    /// - [`CoolProp::set_debug_level`](Self::set_debug_level)
    #[must_use]
    pub fn get_debug_level() -> u8 {
        let level = unsafe { COOLPROP.lock().unwrap().get_debug_level() };
        level.clamp(MIN_DEBUG_LEVEL.into(), MAX_DEBUG_LEVEL.into()) as u8
    }

    /// Sets the `CoolProp` debug level.
    ///
    /// Controls the verbosity of debugging output for subsequent `CoolProp` operations.
    ///
    /// # Arguments
    ///
    /// - `level` -- debug level to set (valid range: `0-10`, larger values produce more verbose
    ///   output)
    ///   - `0` -- disable debugging output
    ///   - `> 0` -- enable debugging output with corresponding verbosity level
    ///
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use rfluids::prelude::*;
    ///
    /// // Set maximum verbosity
    /// CoolProp::set_debug_level(10);
    /// assert_eq!(CoolProp::get_debug_level(), 10);
    ///
    /// // Disable debugging output
    /// CoolProp::set_debug_level(0);
    /// assert_eq!(CoolProp::get_debug_level(), 0);
    /// ```
    ///
    /// # See Also
    ///
    /// - [`CoolProp::get_debug_level`](Self::get_debug_level)
    pub fn set_debug_level(level: u8) {
        unsafe {
            COOLPROP
                .lock()
                .unwrap()
                .set_debug_level(level.clamp(MIN_DEBUG_LEVEL, MAX_DEBUG_LEVEL).into());
        }
    }

    /// Returns a global string parameter from `CoolProp`.
    ///
    /// Returns [`None`] if the parameter key is invalid or the value could not be retrieved.
    ///
    /// # Arguments
    ///
    /// - `param` -- global parameter key _(raw [`&str`](str) or
    ///   [`GlobalParam`](crate::io::GlobalParam))_
    ///
    /// Known parameter keys:
    ///
    /// - `"version"` -- `CoolProp` library version
    /// - `"gitrevision"` -- `git` commit hash of the `CoolProp` build
    /// - `"HOME"` -- `CoolProp` home directory path
    /// - `"REFPROP_version"` -- `REFPROP` version, if available
    /// - `"errstring"` -- pending error message
    /// - `"warnstring"` -- pending warning message
    /// - `"fluids_list"` -- list of all available pure and pseudo-pure substances
    ///   _(comma-separated)_
    /// - `"incompressible_list_pure"` -- list of all available pure incompressible substances
    ///   _(comma-separated)_
    /// - `"incompressible_list_solution"` -- list of all available incompressible binary mixtures
    ///   _(comma-separated)_
    /// - `"predefined_mixtures"` -- list of all available predefined mixtures _(comma-separated)_
    /// - `"cubic_fluids_list"` -- list of all substances for which a cubic EOS is applicable
    ///   _(comma-separated)_
    /// - `"mixture_binary_pairs_list"` -- list of binary pairs for mixtures _(comma-separated)_
    /// - `"parameter_list"` -- list of all available fluid parameters _(comma-separated)_
    /// - `"cubic_fluids_schema"` -- JSON schema for substances with cubic EOS
    /// - `"pcsaft_fluids_schema"` -- JSON schema for substances with PC-SAFT EOS
    ///
    /// # Examples
    ///
    /// ```
    /// use rfluids::prelude::*;
    ///
    /// let coolprop_version = CoolProp::get_global_param("version");
    /// assert!(coolprop_version.is_some());
    /// ```
    ///
    /// # See Also
    ///
    /// - [CoolProp High-Level API](https://coolprop.org/coolprop/HighLevelAPI.html)
    /// - [`CoolPropLib.h` Reference](https://coolprop.org/_static/doxygen/html/_cool_prop_lib_8h.html)
    /// - [`GlobalParam`](crate::io::GlobalParam)
    #[must_use]
    pub fn get_global_param(param: impl AsRef<str>) -> Option<String> {
        let param = param.as_ref().trim();
        let capacity = match param {
            "version" | "gitrevision" | "HOME" | "REFPROP_version" => 100,
            "errstring" | "warnstring" => 500,
            _ => 30_000,
        };
        let param = CString::new(param).unwrap();
        let mut res = StringBuffer::with_capacity(capacity);
        let status = unsafe {
            COOLPROP.lock().unwrap().get_global_param_string(
                param.as_ptr(),
                res.as_mut_ptr(),
                res.capacity(),
            )
        };
        let res: String = res.into();
        if status != 1 || res.trim().is_empty() { None } else { Some(res) }
    }

    /// Returns a string parameter for a specific substance from `CoolProp`.
    ///
    /// Returns [`None`] if the input is invalid or the value could not be retrieved.
    ///
    /// # Arguments
    ///
    /// - `composition_id` -- names of the substance components separated by the `&` symbol or just
    ///   a single substance name _(raw [`&str`](str) or
    ///   [`Substance::composition_id`](crate::substance::Substance::composition_id))_
    /// - `param` -- substance parameter key _(raw [`&str`](str) or
    ///   [`SubstanceParam`](crate::io::SubstanceParam))_
    ///
    /// Known parameter keys:
    ///
    /// - `"name"` -- substance name
    /// - `"aliases"` -- list of aliases for the substance _(comma-separated)_
    /// - `"REFPROP_name"` -- substance name used in `REFPROP`
    /// - `"CAS"` -- Chemical Abstracts Service (CAS) registry number
    /// - `"InChI"` -- International Chemical Identifier
    /// - `"InChIKey"` -- hashed version of the International Chemical Identifier
    /// - `"CHEMSPIDER_ID"` -- [`ChemSpider`](https://www.chemspider.com/) identifier
    /// - `"SMILES"` -- Simplified Molecular Input Line Entry System (SMILES) string
    /// - `"ASHRAE34"` -- ASHRAE Standard 34 safety rating
    /// - `"2DPNG_URL"` -- URL to a `2D` molecular structure image
    /// - `"BibTeX-XXX"` -- BibTeX key, where `XXX` is one of the following:
    ///     - `EOS` -- equation of state reference
    ///     - `CP0` -- ideal gas specific heat equation reference
    ///     - `CONDUCTIVITY` -- thermal conductivity equation reference
    ///     - `MELTING_LINE` -- melting line equation reference
    ///     - `SURFACE_TENSION` -- surface tension equation reference
    ///     - `VISCOSITY` -- viscosity equation reference
    /// - `"pure"` -- `"true"` if the substance is pure, `"false"` otherwise
    /// - `"formula"` -- chemical formula of the substance in LaTeX form _(if available)_
    /// - `"JSON"` -- JSON representation of the substance metadata as stored in `CoolProp`
    ///
    /// # Examples
    ///
    /// ```
    /// use rfluids::prelude::*;
    ///
    /// let water_formula = CoolProp::get_substance_param("Water", "formula");
    /// assert_eq!(water_formula, Some("H_{2}O_{1}".to_string()));
    /// ```
    ///
    /// # See Also
    ///
    /// - [CoolProp High-Level API](https://coolprop.org/coolprop/HighLevelAPI.html)
    /// - [Substance Information](https://coolprop.org/coolprop/HighLevelAPI.html#fluid-information)
    /// - [`CoolPropLib.h` Reference](https://coolprop.org/_static/doxygen/html/_cool_prop_lib_8h.html)
    /// - [`Substance`](crate::substance::Substance)
    /// - [`SubstanceParam`](crate::io::SubstanceParam)
    #[must_use]
    pub fn get_substance_param(
        composition_id: impl AsRef<str>,
        param: impl AsRef<str>,
    ) -> Option<String> {
        let composition_id = composition_id.as_ref().trim();
        let param = param.as_ref().trim();
        let capacity = match param {
            "pure" => 6,
            "InChIKey" | "INCHI_Key" | "INCHIKEY" => 30,
            "name" | "REFPROP_name" | "REFPROPName" | "REFPROPname" | "CAS" | "CAS_number"
            | "ASHRAE34" => 100,
            "JSON" => 500_000,
            _ => 500,
        };
        let composition_id = CString::new(composition_id).unwrap();
        let param = CString::new(param).unwrap();
        let mut res = StringBuffer::with_capacity(capacity);
        let status = unsafe {
            COOLPROP.lock().unwrap().get_fluid_param_string(
                composition_id.as_ptr(),
                param.as_ptr(),
                res.as_mut_ptr(),
                res.capacity(),
            )
        };
        let res: String = res.into();
        if status != 1 || res.trim().is_empty() { None } else { Some(res) }
    }

    /// Sets a configuration key-value pair in `CoolProp`.
    ///
    /// Configuration keys control various aspects of `CoolProp` behavior (e.g., path settings,
    /// `REFPROP` integration, calculation behavior, etc.).
    ///
    /// The `value` argument can accept owned values ([`bool`], [`f64`], [`char`]) which are copied,
    /// or borrowed optional path references ([`&Path`](std::path::Path),
    /// [`&PathBuf`](std::path::PathBuf), [`Option<&Path>`](std::path::Path),
    /// [`Option<&PathBuf>`](std::path::PathBuf)) which must remain valid for the duration of
    /// the call.
    ///
    /// # Arguments
    ///
    /// - `key` -- configuration key _(raw [`&str`](str) or [`ConfigKey`](crate::io::ConfigKey))_
    /// - `value` -- configuration value _([`bool`], [`f64`], [`char`], [`&Path`](std::path::Path),
    ///   [`&PathBuf`](std::path::PathBuf), [`Option<&Path>`](std::path::Path), or
    ///   [`Option<&PathBuf>`](std::path::PathBuf), automatically converted to [`ConfigValue`])_
    ///
    /// Known configuration keys:
    ///
    /// - **Calculation Behavior:**
    ///     - `"ASSUME_CRITICAL_POINT_STABLE"` -- if `true`, evaluation of the stability of critical
    ///       point will be skipped and point will be assumed to be stable. **Default:** `false`
    ///     - `"CRITICAL_SPLINES_ENABLED"` -- if `true`, the critical splines will be used in the
    ///       near-vicinity of the critical point. **Default:** `true`
    ///     - `"CRITICAL_WITHIN_1UK"` -- if `true`, any temperature within `1 uK` of the critical
    ///       temperature will be considered to be **AT** the critical point. **Default:** `true`
    ///     - `"DONT_CHECK_PROPERTY_LIMITS"` -- if `true`, when possible, `CoolProp` will skip
    ///       checking whether values are inside the property limits. **Default:** `false`
    ///     - `"ENABLE_SUPERANCILLARIES"` -- if `true`, the superancillary functions will be used
    ///       for VLE of pure fluids. **Default:** `true`
    ///     - `"HENRYS_LAW_TO_GENERATE_VLE_GUESSES"` -- if `true`, when doing water-based mixture
    ///       dewpoint calculations, use Henry’s Law to generate guesses for liquid-phase
    ///       composition. **Default:** `false`
    ///     - `"NORMALIZE_GAS_CONSTANTS"` -- if `true`, for mixtures, the molar gas constant _(R)_
    ///       will be set to the CODATA value. **Default:** `true`
    ///     - `"OVERWRITE_BINARY_INTERACTION"` -- if `true`, and a pair of binary interaction pairs
    ///       to be added is already there, rather than not adding the binary interaction pair _(and
    ///       probably throwing an exception)_, overwrite it. **Default:** `false`
    ///     - `"OVERWRITE_DEPARTURE_FUNCTION"` -- if `true`, and a departure function to be added is
    ///       already there, rather than not adding the departure function _(and probably throwing
    ///       an exception)_, overwrite it. **Default:** `false`
    ///     - `"OVERWRITE_FLUIDS"` -- if `true`, and a fluid is added to the fluids library that is
    ///       already there, rather than not adding the fluid _(and probably throwing an
    ///       exception)_, overwrite it. **Default:** `false`
    ///     - `"PHASE_ENVELOPE_STARTING_PRESSURE_PA"` -- starting pressure in `Pa` for phase
    ///       envelope construction. **Default:** `100.0`
    ///     - `"R_U_CODATA"` -- the value for the ideal gas constant in `J/mol/K` according to
    ///       CODATA 2022. This value is used to harmonize all the ideal gas constants. This is
    ///       especially important in the critical region. **Default:** `8.314_462_618_153_24`
    ///     - `"SPINODAL_MINIMUM_DELTA"` -- the minimal delta to be used in tracing out the
    ///       spinodal; make sure that the EOS has a spinodal at this value of `delta=rho/rho_r`.
    ///       **Default:** `0.5`
    ///     - `"USE_GUESSES_IN_PROPSSI"` -- if `true`, calls to the vectorized versions of `PropsSI`
    ///       use the previous state as guess value while looping over the input vectors, only makes
    ///       sense when working with a single fluid and with points that are not too far from each
    ///       other. **Default:** `false`
    ///
    /// - **`REFPROP` Integration:**
    ///     - `"ALTERNATIVE_REFPROP_PATH"` -- an alternative path to be provided to the directory
    ///       that contains `REFPROP`’s fluids and mixtures directories. If provided, the `SETPATH`
    ///       function will be called with this directory prior to calling any `REFPROP` functions.
    ///       **Default:** `None`
    ///     - `"ALTERNATIVE_REFPROP_LIBRARY_PATH"` -- an alternative path to the shared library
    ///       file. If provided, it will be used to load `REFPROP`. **Default:** `None`
    ///     - `"ALTERNATIVE_REFPROP_HMX_BNC_PATH"` -- an alternative path to the `HMX.BNC` file. If
    ///       provided, it will be passed into `REFPROP`’s `SETUP` or `SETMIX` routines. _Default:
    ///       `None`
    ///     - `"REFPROP_DONT_ESTIMATE_INTERACTION_PARAMETERS"` --  if `true`, if the binary
    ///       interaction parameters in `REFPROP` are estimated, throw an error rather than silently
    ///       continuing. **Default:** `false`
    ///     - `"REFPROP_IGNORE_ERROR_ESTIMATED_INTERACTION_PARAMETERS"` -- if `true`, if the binary
    ///       interaction parameters in `REFPROP` are unable to be estimated, silently continue
    ///       rather than failing. **Default:** `false`
    ///     - `"REFPROP_USE_GERG"` -- if `true`, rather than using the highly-accurate pure fluid
    ///       equations of state, use the pure-fluid EOS from `GERG-2008`. **Default:** `false`
    ///     - `"REFPROP_USE_PENGROBINSON"` --  if `true`, rather than using the highly-accurate pure
    ///       fluid equations of state, use the Peng-Robinson EOS. **Default:** `false`
    ///
    /// - **Miscellaneous:**
    ///     - `"ALTERNATIVE_TABLES_DIRECTORY"` -- if provided, this path will be the root directory
    ///       for the tabular data. Otherwise, `${HOME}/.CoolProp/Tables` is used. **Default:**
    ///       `None`
    ///     - `"FLOAT_PUNCTUATION"` -- the delimiter to be used as the separator between the number
    ///       fraction. **Default:** `'.'`
    ///     - `"LIST_STRING_DELIMITER"` -- the delimiter to be used when converting a list of
    ///       strings to a string. **Default:** `','`
    ///     - `"MAXIMUM_TABLE_DIRECTORY_SIZE_IN_GB"` -- the maximum allowed size of the directory
    ///       that is used to store tabular data. **Default:** `1.0`
    ///     - `"SAVE_RAW_TABLES"` -- if `true`, the raw, uncompressed tables will also be written to
    ///       file. **Default:** `false`
    ///     - `"VTPR_ALWAYS_RELOAD_LIBRARY"` -- if `true`, the library will always be reloaded, no
    ///       matter what is currently loaded. **Default:** `false`
    ///     - `"VTPR_UNIFAC_PATH"` -- the path to the directory containing the UNIFAC JSON files.
    ///       **Default:** `None`
    ///
    /// # Errors
    ///
    /// Returns [`CoolPropError`](crate::native::CoolPropError) if the configuration
    /// value is invalid.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::path::Path;
    ///
    /// use rfluids::prelude::*;
    ///
    /// // Set configuration values of different types
    /// assert!(CoolProp::set_config("DONT_CHECK_PROPERTY_LIMITS", true).is_ok());
    /// assert!(CoolProp::set_config("PHASE_ENVELOPE_STARTING_PRESSURE_PA", 1e5).is_ok());
    /// assert!(CoolProp::set_config("LIST_STRING_DELIMITER", ';').is_ok());
    /// assert!(
    ///     CoolProp::set_config("ALTERNATIVE_REFPROP_PATH", Path::new("/path/to/refprop")).is_ok()
    /// );
    ///
    /// // Attempt to set invalid configuration values
    /// assert!(CoolProp::set_config("DONT_CHECK_PROPERTY_LIMITS", 'y').is_err());
    /// assert!(CoolProp::set_config("PHASE_ENVELOPE_STARTING_PRESSURE_PA", '0').is_err());
    /// assert!(CoolProp::set_config("LIST_STRING_DELIMITER", 42.0).is_err());
    /// assert!(CoolProp::set_config("ALTERNATIVE_REFPROP_PATH", true).is_err());
    ///
    /// // Attempt to set non-existing configuration key.
    /// // CoolProp ignores nonexistent keys
    /// assert!(CoolProp::set_config("NON_EXISTING_KEY", false).is_ok());
    /// ```
    ///
    /// # Notes
    ///
    /// - Configuration variables can also be set via environment variables by prefixing the key
    ///   name with `COOLPROP_` _(e.g., `COOLPROP_DONT_CHECK_PROPERTY_LIMITS`)_
    /// - Configuration changes affect all subsequent `CoolProp` operations
    ///
    /// # See Also
    ///
    /// - [`CoolProp` Configuration](https://coolprop.org/coolprop/Configuration.html)
    /// - [`CoolPropLib.h` Reference](https://coolprop.org/_static/doxygen/html/_cool_prop_lib_8h.html)
    /// - [`ConfigKey`](crate::io::ConfigKey)
    /// - [`ConfigValue`]
    pub fn set_config<'a>(key: impl AsRef<str>, value: impl Into<ConfigValue<'a>>) -> Result<()> {
        let key = key.as_ref().trim();
        let value = value.into();
        set_config(key, &value)
    }
}

// Code coverage trick
fn set_config(key: &str, value: &ConfigValue) -> Result<()> {
    let key = CString::new(key).unwrap();
    let lock = COOLPROP.lock().unwrap();
    match value {
        ConfigValue::Bool(val) => unsafe {
            lock.set_config_bool(key.as_ptr(), *val);
        },
        ConfigValue::Float(val) => unsafe {
            lock.set_config_double(key.as_ptr(), *val);
        },
        ConfigValue::Char(val) => {
            let c_string = CString::new(val.to_string()).unwrap();
            unsafe {
                lock.set_config_string(key.as_ptr(), c_string.as_ptr());
            }
        }
        ConfigValue::OptionPath(val) => {
            let path = val.map_or_else(String::new, |p| p.to_string_lossy().into_owned());
            let c_string = CString::new(path).unwrap();
            unsafe {
                lock.set_config_string(key.as_ptr(), c_string.as_ptr());
            }
        }
    }
    let error = get_error(&lock);
    error.map_or(Ok(()), Err)
}

#[cfg(test)]
mod tests {
    use rstest::*;

    use super::*;
    use crate::{
        io::{GlobalParam::*, SubstanceParam::*},
        substance::Pure,
    };

    #[test]
    fn get_debug_level() {
        // When
        let res = CoolProp::get_debug_level();

        // Then
        assert_eq!(res, MIN_DEBUG_LEVEL);
    }

    #[test]
    fn set_debug_level() {
        // When
        CoolProp::set_debug_level(MIN_DEBUG_LEVEL);
        let res = CoolProp::get_debug_level();

        // Then
        assert_eq!(res, MIN_DEBUG_LEVEL);
    }

    #[rstest]
    #[case(Version, true)]
    #[case(GitRevision, true)]
    #[case(HomePath, true)]
    #[case(PendingWarning, false)]
    #[case(PureList, true)]
    #[case(IncompPureList, true)]
    #[case(BinaryMixList, true)]
    #[case(PredefinedMixList, true)]
    #[case(CubicList, true)]
    #[case(MixBinaryPairsList, true)]
    #[case(ParamList, true)]
    #[case(CubicJsonSchema, true)]
    #[case(PcSaftJsonSchema, true)]
    #[case("version", true)]
    #[case("gitrevision", true)]
    #[case("HOME", true)]
    #[case("warnstring", false)]
    #[case("fluids_list", true)]
    #[case("incompressible_list_pure", true)]
    #[case("incompressible_list_solution", true)]
    #[case("predefined_mixtures", true)]
    #[case("cubic_fluids_list", true)]
    #[case("mixture_binary_pairs_list", true)]
    #[case("parameter_list", true)]
    #[case("cubic_fluids_schema", true)]
    #[case("pcsaft_fluids_schema", true)]
    #[case("", false)]
    #[case(" ", false)]
    #[case("Hello, World!", false)]
    fn get_global_param(#[case] param: impl AsRef<str>, #[case] is_some: bool) {
        // When
        let res = CoolProp::get_global_param(param);

        // Then
        assert_eq!(res.is_some(), is_some);
    }

    #[rstest]
    #[case(Name, true)]
    #[case(Aliases, true)]
    #[case(RefpropName, true)]
    #[case(Cas, true)]
    #[case(Inchi, true)]
    #[case(InchiKey, true)]
    #[case(ChemSpiderId, true)]
    #[case(Smiles, true)]
    #[case(Ashrae34, true)]
    #[case(TwoDPngUrl, true)]
    #[case(BibtexEos, true)]
    #[case(BibtexCp0, false)]
    #[case(BibtexConductivity, true)]
    #[case(BibtexMeltingLine, true)]
    #[case(BibtexSurfaceTension, true)]
    #[case(BibtexViscosity, true)]
    #[case(IsPure, true)]
    #[case(Formula, true)]
    #[case(Metadata, true)]
    #[case("name", true)]
    #[case("aliases", true)]
    #[case("REFPROP_name", true)]
    #[case("CAS", true)]
    #[case("InChI", true)]
    #[case("InChIKey", true)]
    #[case("CHEMSPIDER_ID", true)]
    #[case("SMILES", true)]
    #[case("ASHRAE34", true)]
    #[case("2DPNG_URL", true)]
    #[case("BibTeX-EOS", true)]
    #[case("BibTeX-CP0", false)]
    #[case("BibTeX-CONDUCTIVITY", true)]
    #[case("BibTeX-MELTING_LINE", true)]
    #[case("BibTeX-SURFACE_TENSION", true)]
    #[case("BibTeX-VISCOSITY", true)]
    #[case("pure", true)]
    #[case("formula", true)]
    #[case("JSON", true)]
    #[case("", false)]
    #[case(" ", false)]
    #[case("Hello, World!", false)]
    fn get_substance_param(#[case] param: impl AsRef<str>, #[case] is_some: bool) {
        // Given
        let substance = Pure::Water;

        // When
        let res = CoolProp::get_substance_param(substance, param);

        // Then
        assert_eq!(res.is_some(), is_some);
    }
}