mirror of
https://github.com/CoolProp/CoolProp.git
synced 2026-04-23 03:00:17 -04:00
1043 lines
54 KiB
C++
1043 lines
54 KiB
C++
// CoolPropMathcad.cpp : Defines the exported functions for the DLL Add-in.
|
|
//
|
|
|
|
#include <string>
|
|
#include <cstring>
|
|
|
|
#ifndef NOMINMAX // Kill windows' horrible min() and max() macros
|
|
# define NOMINMAX
|
|
#endif
|
|
#include "mcadincl.h"
|
|
#undef NOMINMAX
|
|
|
|
enum
|
|
{
|
|
MC_STRING = STRING
|
|
}; // substitute enumeration variable MC_STRING for STRING, use MC_STRING below
|
|
#undef STRING // undefine STRING as it conflicts with STRING enum in fmtlib/format.h
|
|
|
|
#include "CoolProp.h"
|
|
#include "DataStructures.h"
|
|
#include "HumidAirProp.h"
|
|
#include <Backends/Helmholtz/MixtureParameters.h>
|
|
|
|
// Setup Dialog Window for debugging
|
|
HWND hwndDlg; // Generic Dialog handle for pop-up message boxes (MessageBox) when needed
|
|
|
|
namespace CoolProp {
|
|
extern void apply_simple_mixing_rule(const std::string& identifier1, const std::string& identifier2, const std::string& rule);
|
|
}
|
|
|
|
enum EC
|
|
{
|
|
MUST_BE_REAL = 1, // Mathcad Error Codes v
|
|
INSUFFICIENT_MEMORY,
|
|
INTERRUPTED,
|
|
ONLY_ONE_COLUMN,
|
|
UNEQUAL_LENGTH,
|
|
//---------------------------------------------------
|
|
BAD_FLUID, // CoolProp Error Codes from here v
|
|
BAD_MIXTURE,
|
|
BAD_IF97_FLUID,
|
|
BAD_PARAMETER,
|
|
BAD_PHASE,
|
|
ONLY_ONE_PHASE_SPEC,
|
|
BAD_REF,
|
|
NON_TRIVIAL,
|
|
NO_REFPROP,
|
|
NOT_AVAIL,
|
|
BAD_INPUT_PAIR,
|
|
BAD_QUAL,
|
|
TWO_PHASE,
|
|
NON_TWO_PHASE,
|
|
T_OUT_OF_RANGE,
|
|
P_OUT_OF_RANGE,
|
|
H_OUT_OF_RANGE,
|
|
S_OUT_OF_RANGE,
|
|
TP_SATURATION,
|
|
HA_INPUTS,
|
|
BAD_BINARY_PAIR,
|
|
MISSING_BINARY_PAIR,
|
|
BAD_RULE,
|
|
PAIR_EXISTS,
|
|
NO_SOLUTION,
|
|
UNKNOWN,
|
|
NUMBER_OF_ERRORS
|
|
}; // Dummy Code for Error Count
|
|
|
|
// table of error messages
|
|
// As of Mathcad Prime 10, these are now actually returned as Custom Error: messages
|
|
char* CPErrorMessageTable[NUMBER_OF_ERRORS] = {"Argument must be real",
|
|
"Insufficient Memory",
|
|
"Interrupted",
|
|
"Only one column allowed in input array",
|
|
"Input arrays must be the same length",
|
|
"Invalid Fluid String",
|
|
"Invalid predefined mixture",
|
|
"IF97 Backend supports pure \"Water\" only",
|
|
"Invalid Parameter String",
|
|
"Invalid Phase String",
|
|
"Only one input key phase specification allowed",
|
|
"Cannot use this REF State with this fluid",
|
|
"Input Parameter is Non-Trivial",
|
|
"REFPROP not installed correctly",
|
|
"This Output parameter is not available for this Fluid",
|
|
"This Input Pair is not yet support for this Fluid",
|
|
"Input vapor quality must be between 0 and 1",
|
|
"Output variable not valid in two phase region",
|
|
"Output variable only valid in two phase region",
|
|
"Temperature out of range",
|
|
"Pressure out of range",
|
|
"Enthalpy out of range",
|
|
"Entropy out of range",
|
|
"Temperature-Pressure inputs in 2-phase region; use TQ or PQ",
|
|
"At least one of the inputs must be [T], [R], [W], or [Tdp]",
|
|
"Could not match binary pair",
|
|
"Missing at least one set of binary interaction parameters.",
|
|
"Mixing rule must be \"linear\" or \"Lorentz-Berthelot\".",
|
|
"Specified binary pair already exists.",
|
|
"No solution found for the given inputs and fluid.",
|
|
"CoolProp Issue: Use get_global_param_string(\"errstring\") for more info.",
|
|
"Error Count - Not Used"};
|
|
|
|
// Helper: allocate Mathcad string and copy contents
|
|
static char* AllocMathcadString(const std::string& s) {
|
|
// Must use MathcadAllocate(size) so Mathcad can track and release the memory properly.
|
|
char* c = MathcadAllocate(static_cast<int>(s.size()) + 1);
|
|
if (c != nullptr) {
|
|
// copy s into c, this process avoids the const-cast type which would result from instead
|
|
// converting the string using s.c_str()
|
|
// memcpy is fine for this because std::string in C++11+ is contiguous
|
|
std::memcpy(c, s.data(), s.size());
|
|
c[s.size()] = '\0';
|
|
}
|
|
return c;
|
|
}
|
|
|
|
// Helper: allocate Mathcad array and copy a vector-of-vectors into its real part
|
|
static LRESULT AllocateToMathcadArray(LPCOMPLEXARRAY dest, const std::vector<std::vector<double>>& Vec) {
|
|
// Expect Vec to be non-empty (caller checks ValidNumber(Vec[0][0]) earlier)
|
|
const size_t rows = Vec.size();
|
|
const size_t cols = (rows > 0) ? Vec[0].size() : 0;
|
|
|
|
// Make sure dest is an actual pointer before trying to allocate memory for it
|
|
if (dest == nullptr) {
|
|
return MAKELRESULT(INSUFFICIENT_MEMORY, 0); // Return error if dest is not a valid pointer
|
|
}
|
|
|
|
// Allocate Mathcad array memory (real part only)
|
|
if (!MathcadArrayAllocate(dest,
|
|
static_cast<int>(rows), // rows = number of output variables
|
|
static_cast<int>(cols), // cols = number of input values
|
|
TRUE, // allocate memory for the real part
|
|
FALSE)) // do not allocate memory for the imaginary part
|
|
{
|
|
return MAKELRESULT(INSUFFICIENT_MEMORY, 1);
|
|
}
|
|
|
|
// Copy contents into the allocated real matrix
|
|
for (size_t irow = 0; irow < rows; ++irow) {
|
|
const auto& row = Vec[irow];
|
|
// Assume each inner vector has 'cols' elements (consistent with PropsSImulti contract)
|
|
for (size_t icol = 0; icol < cols; ++icol) {
|
|
dest->hReal[icol][irow] = row[icol]; // Note the indexing order for Mathcad arrays: hReal[column][row]
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Helper: Get IEEE 754 double precision NaN value for returning in case of errors in array outputs
|
|
static double get_nan() {
|
|
unsigned long long nan_pattern = 0xFFF8000000000000ULL;
|
|
return *(double*)&nan_pattern;
|
|
}
|
|
|
|
// Helper: check that a complex scalar input is Real and return proper Mathcad error
|
|
static inline LRESULT CheckRealOrError(LPCCOMPLEXSCALAR val, int position) {
|
|
if (val->imag != 0.0) return MAKELRESULT(MUST_BE_REAL, position);
|
|
return 0;
|
|
}
|
|
|
|
// Helper: check that a complex array input is Real and return proper Mathcad error
|
|
static inline LRESULT CheckRealArrayOrError(LPCCOMPLEXARRAY val, int position) {
|
|
if (val->cols != 1) return MAKELRESULT(ONLY_ONE_COLUMN, position);
|
|
if (val->hImag != NULL) return MAKELRESULT(MUST_BE_REAL, position);
|
|
return 0;
|
|
}
|
|
|
|
// Helper: Determine which of the valid delimiters were passed in a String.
|
|
static inline char FindDelimiter(std::string instr) {
|
|
unsigned int nDelims = 0; // Delimiter count
|
|
static char dType = '\0'; // Initialize to 'null' character, which will be returned if no delimiters are found.
|
|
static const char dels[] = {' ', ',', '&', ';', '|'}; // Valid delimiters list
|
|
|
|
for (char d : dels) { // Loop through valid delimiters
|
|
if (instr.find(d) != std::string::npos) { // If found
|
|
nDelims++; // increment
|
|
dType = d; // and save the delimiter type
|
|
}
|
|
}
|
|
|
|
if (nDelims > 1) dType = 'E'; // Multiple delimiters found, return 'null'E' character to trigger error
|
|
|
|
return dType; // Return the delimiter type found, or '\0' if none, or 'E' if multiple
|
|
}
|
|
|
|
// This code executes the user function CP_get_global_param_string, which is a wrapper for
|
|
// the CoolProp.get_global_param_string() function, used to get a global string parameter from CoolProp
|
|
static LRESULT CP_get_global_param_string(LPMCSTRING ParamValue, // output (value of parameter)
|
|
LPCMCSTRING ParamName) // name of parameter (string) to retrieve
|
|
{
|
|
std::string s;
|
|
// Invoke the std::string form of get_global_param_string() function, save result to a new string s
|
|
try {
|
|
s = CoolProp::get_global_param_string(ParamName->str);
|
|
} catch (const CoolProp::ValueError& e) {
|
|
std::string emsg(e.what());
|
|
CoolProp::set_error_string(emsg);
|
|
if (emsg.find("parameter") != std::string::npos)
|
|
return MAKELRESULT(BAD_PARAMETER, 1);
|
|
else
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
}
|
|
|
|
// Must use MathcadAllocate(size) so Mathcad can track and release, using Helper routine above
|
|
char* c = AllocMathcadString(s);
|
|
// assign the string to the function's output parameter
|
|
ParamValue->str = c;
|
|
|
|
// normal return
|
|
return 0;
|
|
}
|
|
|
|
// This code executes the user function CP_get_fluid_param_string, which is a wrapper for
|
|
// the CoolProp.get_fluid_param_string() function, used to get a fluid string parameter from CoolProp
|
|
static LRESULT CP_get_fluid_param_string(LPMCSTRING ParamValue, // output (value of parameter)
|
|
LPCMCSTRING FluidName, // name of fluid (string) to retrieve
|
|
LPCMCSTRING ParamName) // name of parameter (string) to retrieve
|
|
{
|
|
std::string s;
|
|
// Invoke the std::string form of get_fluid_param_string() function, save result to a new string s
|
|
try {
|
|
s = CoolProp::get_fluid_param_string(FluidName->str, ParamName->str);
|
|
} catch (const CoolProp::ValueError& e) {
|
|
// MSGBOX(e.what());
|
|
std::string emsg(e.what());
|
|
CoolProp::set_error_string(emsg);
|
|
if (emsg.find("key") != std::string::npos)
|
|
return MAKELRESULT(BAD_FLUID, 1);
|
|
else if (emsg.find("parameter") != std::string::npos)
|
|
return MAKELRESULT(BAD_PARAMETER, 2);
|
|
else
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
}
|
|
|
|
// Must use MathcadAllocate(size) so Mathcad can track and release, using Helper routine above
|
|
char* c = AllocMathcadString(s);
|
|
// assign the string to the function's output parameter
|
|
ParamValue->str = c;
|
|
|
|
// normal return
|
|
return 0;
|
|
}
|
|
|
|
// This code executes the user function CP_set_reference_state, which is a wrapper for
|
|
// the CoolProp.set_reference_stateS() function, used to set the H/S reference states
|
|
// based on a standard state string of "IIR", "ASHRAE", "NBP", or "DEF".
|
|
static LRESULT CP_set_reference_state(LPCOMPLEXSCALAR Conf, // output (dummy value)
|
|
LPCMCSTRING FluidName, // name of fluid (string) to retrieve
|
|
LPCMCSTRING StateStr) // name of standard state (string) to set
|
|
{
|
|
// Invoke the set_reference_stateS() function, no result from this void function.
|
|
try {
|
|
CoolProp::set_reference_stateS(FluidName->str, StateStr->str);
|
|
} catch (const CoolProp::ValueError& e) {
|
|
std::string emsg(e.what());
|
|
CoolProp::set_error_string(emsg);
|
|
|
|
// Normalize the error message to lowercase to simplify substring checks and
|
|
// avoid repeating checks for different capitalizations.
|
|
std::string emsg_l = emsg;
|
|
std::transform(emsg_l.begin(), emsg_l.end(), emsg_l.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
|
|
|
if (emsg_l.find("key") != std::string::npos)
|
|
return MAKELRESULT(BAD_FLUID, 1);
|
|
else if ((emsg_l.find("cannot use") != std::string::npos) || (emsg_l.find("temperature to qt_flash") != std::string::npos))
|
|
return MAKELRESULT(BAD_REF, 2);
|
|
else if (emsg_l.find("reference state") != std::string::npos)
|
|
return MAKELRESULT(BAD_PARAMETER, 2);
|
|
else
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
}
|
|
|
|
// assign the dummy return value
|
|
Conf->real = 0;
|
|
|
|
// normal return
|
|
return 0;
|
|
}
|
|
|
|
// Helper: centralize text-matching logic for Props1SI error handling
|
|
static LRESULT HandleProps1SIError(const std::string& emsg, const std::string& FluidString, const std::string& PropName) {
|
|
auto contains = [&](const char* s) { return emsg.find(s) != std::string::npos; };
|
|
std::string mixName = ""; // Temp variable to hold the name of the mixture string argument for error checking, if needed
|
|
unsigned int errPos = 0; // Temp variable to hold the position of the error argument for returning to Mathcad, if needed
|
|
|
|
// Check for "valid fluid" error first, since it is the most common error and can have multiple causes
|
|
// that require parsing the error message to determine the specific error type and position.
|
|
if (contains("valid fluid")) {
|
|
if (contains("Neither input")) {
|
|
if (contains("REFPROP")) { // REFPROP or REFPROP Fluid not found
|
|
errPos = (FluidString.find("REFPROP") != std::string::npos) ? 1u : 2u;
|
|
return MAKELRESULT(BAD_FLUID, errPos);
|
|
}
|
|
if (contains("IF97")) { // Something other than "Water" was used to IF97
|
|
errPos = (FluidString.find("IF97") != std::string::npos) ? 1u : 2u;
|
|
return MAKELRESULT(BAD_IF97_FLUID, errPos);
|
|
}
|
|
if (lower(FluidString).find(".mix") != std::string::npos) { // Mixture string error position 1
|
|
errPos = 1u;
|
|
mixName = FluidString;
|
|
} else if (lower(PropName).find(".mix") != std::string::npos) { // Mixture string error position 2
|
|
errPos = 2u;
|
|
mixName = PropName;
|
|
}
|
|
if (!mixName.empty()) {
|
|
Dictionary dict;
|
|
if (!CoolProp::is_predefined_mixture(mixName, dict)) // Mixture string was used, but not found in CoolProp's predefined mixtures
|
|
return MAKELRESULT(BAD_MIXTURE, errPos);
|
|
else
|
|
return MAKELRESULT(MISSING_BINARY_PAIR, errPos); // Likely missing a binary interaction pair.
|
|
}
|
|
return MAKELRESULT(BAD_FLUID, 1);
|
|
} else { // "Both inputs"
|
|
return MAKELRESULT(BAD_PARAMETER, 2);
|
|
}
|
|
}
|
|
// Check for "invalid parameter" errors next...
|
|
// The parameter name will be surrounded by brackets in the error message, so add brackets to the parameter name for searching
|
|
std::string PropNameBrackets = "[" + PropName + "]";
|
|
if (contains("Unable to use")) {
|
|
errPos = (emsg.find(PropNameBrackets) != std::string::npos) ? 2u : 1u;
|
|
if (contains("Output string is invalid")) return MAKELRESULT(BAD_PARAMETER, errPos);
|
|
if (contains("non-trivial")) return MAKELRESULT(NON_TRIVIAL, errPos);
|
|
if (contains("No outputs")) return MAKELRESULT(NOT_AVAIL, errPos);
|
|
return MAKELRESULT(UNKNOWN, errPos);
|
|
}
|
|
// Otherwise, un-trapped error. Return generic error code and have user check get_global_param_string("errstring") for details
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
}
|
|
|
|
// This code executes the user function CP_Props1SI, which is a wrapper for
|
|
// the CoolProp.Props1SI() function, used to simply extract a
|
|
// fluid-specific parameter that is not dependent on the state
|
|
static LRESULT CP_Props1SI(LPCOMPLEXSCALAR Prop, // pointer to the result
|
|
LPCMCSTRING Fluid, // string with a valid CoolProp fluid name
|
|
LPCMCSTRING PropName) // a fluid property
|
|
{
|
|
std::string PropStr(PropName->str);
|
|
std::string FluidString = Fluid->str;
|
|
|
|
// pass the arguments to the CoolProp.Props1() function
|
|
Prop->real = CoolProp::Props1SI(Fluid->str, PropName->str);
|
|
|
|
// Note: Props1SI does not throw exceptions, but instead
|
|
// sets global parameter "errstring" and returns _HUGE.
|
|
// Use ValidNumber(val) to see if Props1SI failed with an error message...
|
|
if (!ValidNumber(Prop->real)) {
|
|
std::string emsg = CoolProp::get_global_param_string("errstring");
|
|
CoolProp::set_error_string(emsg); // reset error string so Mathcad can retrieve it
|
|
return HandleProps1SIError(emsg, FluidString, PropStr);
|
|
}
|
|
|
|
// normal return
|
|
return 0;
|
|
}
|
|
|
|
// Helper: centralize text-matching logic for PropsSI and PhaseSI error handling
|
|
static LRESULT HandlePropsSIError(const std::string& emsg, const std::string& Prop1Name, const unsigned int o) {
|
|
// Parameter "o" is passed as a parameter # offset between PropsSI (6 parameters) and PhaseSI (5 parameters and no output string)
|
|
// PropsSI calls this routine with o = 0u (zero), while PhaseSI calls this routine with o = 1u.
|
|
auto contains = [&](const char* s) { return emsg.find(s) != std::string::npos; };
|
|
unsigned int errPos = 0;
|
|
CoolProp::parameters key1;
|
|
|
|
// Check for invalid input pair specification first.
|
|
if (contains("Input pair variable is invalid")) {
|
|
errPos = !is_valid_parameter(Prop1Name, key1) ? 2u - o : 4u - o;
|
|
return MAKELRESULT(BAD_PARAMETER, errPos);
|
|
}
|
|
if (contains("Input Name1")) return MAKELRESULT(BAD_PARAMETER, 2 - o);
|
|
if (contains("Input Name2")) return MAKELRESULT(BAD_PARAMETER, 4 - o);
|
|
if (contains("Phase can only be specified on one")) return MAKELRESULT(ONLY_ONE_PHASE_SPEC, 4 - o);
|
|
if (contains("valid phase")) {
|
|
errPos = !is_valid_parameter(Prop1Name, key1) ? 2u - o : 4u - o;
|
|
return MAKELRESULT(BAD_PHASE, errPos);
|
|
}
|
|
if (contains("This pair of inputs")) return MAKELRESULT(BAD_INPUT_PAIR, 2 - o);
|
|
if (contains("Input vapor quality")) return (Prop1Name == "Q") ? MAKELRESULT(BAD_QUAL, 3 - o) : MAKELRESULT(BAD_QUAL, 5 - o);
|
|
if (contains("Output string is invalid")) return MAKELRESULT(BAD_PARAMETER, 1);
|
|
if (contains("not valid in two phase region")) return MAKELRESULT(TWO_PHASE, 1);
|
|
if (contains("only defined within the two-phase")) return MAKELRESULT(NON_TWO_PHASE, 1);
|
|
if (contains("not implemented")) return MAKELRESULT(NOT_AVAIL, 1);
|
|
|
|
// Check for invalid fluid errors next.
|
|
if (contains("Predefined mixture") && contains("not found")) return MAKELRESULT(BAD_MIXTURE, 6u - o);
|
|
if (contains("Initialize failed")) {
|
|
errPos = 6u - o;
|
|
if (contains("Could not match the binary pair")) return MAKELRESULT(MISSING_BINARY_PAIR, errPos);
|
|
if (contains("REFPROP")) {
|
|
if (contains("cannot use")) return MAKELRESULT(NO_REFPROP, errPos);
|
|
return MAKELRESULT(BAD_FLUID, errPos);
|
|
}
|
|
if (contains("IF97")) return MAKELRESULT(BAD_IF97_FLUID, errPos);
|
|
return MAKELRESULT(BAD_FLUID, errPos);
|
|
}
|
|
|
|
// Check for out of range errors next.
|
|
if (contains("Temperature") || contains("below Tmelt(p)")) // Cases where temperature is out of range.
|
|
return (Prop1Name == "T") ? MAKELRESULT(T_OUT_OF_RANGE, 3u - o) : MAKELRESULT(T_OUT_OF_RANGE, 5u - o);
|
|
if (contains("Saturation pressure")) // Cases at saturation for T-P
|
|
return (Prop1Name == "P") ? MAKELRESULT(TP_SATURATION, 3u - o) : MAKELRESULT(TP_SATURATION, 5u - o);
|
|
if (contains("Pressure") || contains("melting line T(p)")) // Cases where pressure is out of range
|
|
return (Prop1Name == "P") ? MAKELRESULT(P_OUT_OF_RANGE, 3u - o) : MAKELRESULT(P_OUT_OF_RANGE, 5u - o);
|
|
if (contains("Enthalpy") || contains("solution because Hmolar")) // Cases where enthalpy is out of range
|
|
return ((Prop1Name == "H") || (Prop1Name == "Hmolar")) ? MAKELRESULT(H_OUT_OF_RANGE, 3u - o) : MAKELRESULT(H_OUT_OF_RANGE, 5u - o);
|
|
if (contains("Entropy") || contains("solution because Smolar")) // Cases where entropy is out of range
|
|
return ((Prop1Name == "S") || (Prop1Name == "Smolar")) ? MAKELRESULT(S_OUT_OF_RANGE, 3u - o) : MAKELRESULT(S_OUT_OF_RANGE, 5u - o);
|
|
|
|
// Un-trapped error, return generic error code and have user check get_global_param_string("errstring") for details
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
}
|
|
|
|
// This code executes the user function CP_PropsSI, which is a wrapper for
|
|
// the CoolProp.PropsSI() function, used to extract a fluid-specific parameter that is dependent on the state
|
|
static LRESULT CP_PropsSI(LPCOMPLEXSCALAR Prop, // pointer to the result
|
|
LPCMCSTRING OutputName, // string with a valid CoolProp OutputName
|
|
LPCMCSTRING InputName1, // CoolProp InputName1
|
|
LPCCOMPLEXSCALAR InputProp1, // CoolProp InputProp1
|
|
LPCMCSTRING InputName2, // CoolProp InputName2
|
|
LPCCOMPLEXSCALAR InputProp2, // CoolProp InputProp2
|
|
LPCMCSTRING FluidName) // CoolProp Fluid
|
|
{
|
|
// unsigned int errPos = 0;
|
|
std::string Prop1Name(InputName1->str);
|
|
std::string Prop2Name(InputName2->str);
|
|
std::string FluidString = FluidName->str;
|
|
|
|
// check that the first scalar argument is real
|
|
LRESULT r = CheckRealOrError(InputProp1, 3);
|
|
if (r) return r;
|
|
|
|
// check that the second scalar argument is real
|
|
r = CheckRealOrError(InputProp2, 5);
|
|
if (r) return r;
|
|
|
|
// pass the arguments to the CoolProp.PropsSI() function
|
|
Prop->real = CoolProp::PropsSI(OutputName->str, InputName1->str, InputProp1->real, InputName2->str, InputProp2->real, FluidName->str);
|
|
|
|
// Note: PropsSI does not throw exceptions, but instead
|
|
// sets global parameter "errstring" and returns _HUGE.
|
|
// Use ValidNumber(val) to see if PropsSI failed with an error message...
|
|
if (!ValidNumber(Prop->real)) {
|
|
std::string emsg = CoolProp::get_global_param_string("errstring");
|
|
CoolProp::set_error_string(emsg); // reset error string so Mathcad can retrieve it
|
|
return HandlePropsSIError(emsg, Prop1Name, 0u);
|
|
}
|
|
// normal return
|
|
return 0;
|
|
}
|
|
|
|
// This code executes the user function CP_PropsSImulti, which is a wrapper for
|
|
// the CoolProp.PropsSImulti() function, used to extract a range of fluid-specific parameter dependent on the state ranges
|
|
static LRESULT CP_PropsSImulti(LPCOMPLEXARRAY Prop, // pointer to the result matrix
|
|
LPCMCSTRING OutputName, // string with delimited, valid CoolProp OutputName substrings for each output column
|
|
LPCMCSTRING InputName1, // CoolProp InputName1
|
|
LPCCOMPLEXARRAY InputProp1, // CoolProp InputProp1 (Array)
|
|
LPCMCSTRING InputName2, // CoolProp InputName2
|
|
LPCCOMPLEXARRAY InputProp2, // CoolProp InputProp2 (Array
|
|
LPCMCSTRING FluidName) // CoolProp Fluid
|
|
{
|
|
// unsigned int errPos = 0;
|
|
std::string Prop1Name(InputName1->str);
|
|
std::string Prop2Name(InputName2->str);
|
|
const std::string OutStr(OutputName->str);
|
|
const std::string FluidString(FluidName->str);
|
|
|
|
// check that the first and second array arguments are real and are single-column vectors
|
|
LRESULT r = CheckRealArrayOrError(InputProp1, 3);
|
|
if (r) return r;
|
|
|
|
r = CheckRealArrayOrError(InputProp2, 5);
|
|
if (r) return r;
|
|
|
|
// make sure input arrays are the same length (PropsSImulti expects the same number of values for each input property)
|
|
if (InputProp1->rows != InputProp2->rows) {
|
|
return MAKELRESULT(UNEQUAL_LENGTH, 5); // Return error for InputProp2 if the lengths do not match
|
|
}
|
|
|
|
// Convert the input arrays to vectors for passing to PropsSImulti.
|
|
// The PropsSImulti function expects std::vector<double> for the input properties,
|
|
std::vector<double> Prop1Vec(InputProp1->hReal[0], InputProp1->hReal[0] + InputProp1->rows);
|
|
std::vector<double> Prop2Vec(InputProp2->hReal[0], InputProp2->hReal[0] + InputProp2->rows);
|
|
|
|
//Parse the OutputName string into a vector of output names, splitting on the delimiter character
|
|
const char del = FindDelimiter(OutStr);
|
|
if (del == 'E') { // Multiple delimiters found, return error
|
|
CoolProp::set_error_string("Multiple delimiters found in OutputName string. Please use only one of the following delimiter types: space, "
|
|
"comma, semicolon, ampersand, or pipe.");
|
|
return MAKELRESULT(BAD_PARAMETER, 1);
|
|
}
|
|
const std::vector<std::string> OutNames = strsplit(OutStr, del);
|
|
|
|
//Parse the fluid string to check for multiple fluids for multi-fluid support, splitting on the delimiter character
|
|
std::string backend, fluid;
|
|
CoolProp::extract_backend(FluidName->str, backend, fluid);
|
|
std::vector<double> fractions(1, 1.0);
|
|
// extract_fractions checks for has_fractions_in_string / has_solution_concentration; no need to double check
|
|
const std::string fluid_string = CoolProp::extract_fractions(fluid, fractions);
|
|
|
|
// With vectors obtained, pass the parameters to the CoolProp.PropsSI() function
|
|
std::vector<std::vector<double>> IO =
|
|
CoolProp::PropsSImulti(OutNames, InputName1->str, Prop1Vec, InputName2->str, Prop2Vec, backend, strsplit(fluid_string, '&'), fractions);
|
|
|
|
// Note: PropsSImulti does not throw value exceptions, but instead
|
|
// sets global parameter "errstring" and returns _HUGE for all values that fail.
|
|
// If there is only one input point and one output the return matrix with be empty and
|
|
// we can handle the error with the same logic as PropsSI.
|
|
if (IO.empty() || IO[0].empty()) {
|
|
std::string emsg = CoolProp::get_global_param_string("errstring"); // Also clears the error string
|
|
CoolProp::set_error_string(emsg); // Reset error string so Mathcad can retrieve it
|
|
// MessageBoxA(hwndDlg, emsg.c_str(), "CoolProp PropsSImulti Error", MB_OK | MB_ICONERROR); // Pop up the error for debugging
|
|
return HandlePropsSIError(emsg, Prop1Name, 0u); // Show error without a parameter offset (0u).
|
|
}
|
|
|
|
// Return any _HUGE values as NaN to Mathcad to use NaN filtering in arrays.
|
|
// PropsSImulti returns _HUGE for any output value that fails, and sets the error string accordingly,
|
|
// so this should not interfere with valid outputs. Use the get_nan() helper function above to ensure
|
|
// we get a proper NaN value that is recognized as such by Mathcad.
|
|
double NaN = get_nan();
|
|
for (auto& row : IO) {
|
|
for (auto& val : row) {
|
|
if (!ValidNumber(val)) {
|
|
val = NaN;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy the results from the IO vector of vectors into the output complex array Prop.
|
|
// Must use MathcadArrayAllocate() so Mathcad can track and release the memory properly, using Helper routine above
|
|
LRESULT rc = AllocateToMathcadArray(Prop, IO);
|
|
if (rc) return rc;
|
|
|
|
// normal return
|
|
return 0;
|
|
}
|
|
|
|
// This code executes the user function CP_PhaseSI, which is a wrapper for
|
|
// the CoolProp.PhaseSI() function, used to the fluid phase dependent on the state
|
|
static LRESULT CP_PhaseSI(LPMCSTRING PhaseStr, // output (CoolProp phase string)
|
|
LPCMCSTRING InputName1, // CoolProp InputName1
|
|
LPCCOMPLEXSCALAR InputProp1, // CoolProp InputProp1
|
|
LPCMCSTRING InputName2, // CoolProp InputName2
|
|
LPCCOMPLEXSCALAR InputProp2, // CoolProp InputProp2
|
|
LPCMCSTRING FluidName) // CoolProp Fluid
|
|
{
|
|
std::string ph;
|
|
std::string Prop1Name(InputName1->str);
|
|
std::string Prop2Name(InputName2->str);
|
|
std::string FluidString = FluidName->str;
|
|
|
|
// check that the first scalar argument is real
|
|
LRESULT r = CheckRealOrError(InputProp1, 2);
|
|
if (r) return r;
|
|
|
|
// check that the second scalar argument is real
|
|
r = CheckRealOrError(InputProp2, 4);
|
|
if (r) return r;
|
|
|
|
// pass the arguments to the CoolProp.phaseSI() function
|
|
ph = CoolProp::PhaseSI(InputName1->str, InputProp1->real, InputName2->str, InputProp2->real, FluidName->str);
|
|
|
|
// Note: PhaseSI does not throw exceptions, but instead appends the global parameter "errstring"
|
|
// to the returned phase string if there is an error and returns "unknown" for the phase string.
|
|
//
|
|
// Use string search to see if PhaseSI failed with an error message (appended ": <error>")...
|
|
if (ph.find("unknown:") != std::string::npos) {
|
|
std::string emsg = ph.substr(ph.find(":") + 2, ph.length() - ph.find(":") + 2);
|
|
CoolProp::set_error_string(emsg); // reset error string so Mathcad can retrieve it
|
|
// Use PropsSI error handling logic to determine the specific error message but with adjusted argument
|
|
// positions for PhaseSI (which has no OutputName argument and thus shifts the positions of the InputName
|
|
// and InputProp arguments by 1)
|
|
return HandlePropsSIError(emsg, Prop1Name, 1u);
|
|
}
|
|
|
|
// Must use MathcadAllocate(size) so Mathcad can track and release, using Helper routine above
|
|
char* c = AllocMathcadString(ph);
|
|
// assign the string to the function's output parameter
|
|
PhaseStr->str = c;
|
|
|
|
// normal return
|
|
return 0;
|
|
}
|
|
|
|
// Helper: centralize text-matching logic for HAPropsSI error handling
|
|
static LRESULT HandleHAPropsSIError(const std::string& emsg, const std::string& OutName, const std::string& Prop1Name, const std::string& Prop2Name,
|
|
const std::string& Prop3Name) {
|
|
// Check for explicit parameter mentions first
|
|
if (emsg.find(OutName) != std::string::npos)
|
|
return MAKELRESULT(BAD_PARAMETER, 1);
|
|
else if (emsg.find(Prop1Name) != std::string::npos)
|
|
return MAKELRESULT(BAD_PARAMETER, 2);
|
|
else if (emsg.find(Prop2Name) != std::string::npos)
|
|
return MAKELRESULT(BAD_PARAMETER, 4);
|
|
else if (emsg.find(Prop3Name) != std::string::npos)
|
|
return MAKELRESULT(BAD_PARAMETER, 6);
|
|
|
|
// Specific HAPropsSI input-message case
|
|
if (emsg.find("at least one of the variables") != std::string::npos) return MAKELRESULT(HA_INPUTS, 2);
|
|
|
|
// Fallback: unknown error, let user inspect errstring
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
}
|
|
|
|
// This code executes the user function CP_HAPropsSI, which is a wrapper for
|
|
// the CoolProp.HAPropsSI() function, used to extract humid air properties in base-SI units
|
|
static LRESULT CP_HAPropsSI(LPCOMPLEXSCALAR Prop, // pointer to the result
|
|
LPCMCSTRING OutputName, // string with a valid CoolProp Output Name
|
|
LPCMCSTRING InputName1, // CoolProp InputName1
|
|
LPCCOMPLEXSCALAR InputProp1, // CoolProp InputProp1
|
|
LPCMCSTRING InputName2, // CoolProp InputName2
|
|
LPCCOMPLEXSCALAR InputProp2, // CoolProp InputProp2
|
|
LPCMCSTRING InputName3, // CoolProp InputName3
|
|
LPCCOMPLEXSCALAR InputProp3) // CoolProp InputProp3
|
|
{
|
|
unsigned int errPos = 0;
|
|
std::string OutName(OutputName->str);
|
|
OutName = "[" + OutName + "]";
|
|
std::string Prop1Name(InputName1->str);
|
|
Prop1Name = "[" + Prop1Name + "]";
|
|
std::string Prop2Name(InputName2->str);
|
|
Prop2Name = "[" + Prop2Name + "]";
|
|
std::string Prop3Name(InputName3->str);
|
|
Prop3Name = "[" + Prop3Name + "]";
|
|
|
|
// check that the 3 scalar arguments are real or throw error using inline helper function above
|
|
LRESULT r = CheckRealOrError(InputProp1, 3);
|
|
if (r) return r;
|
|
|
|
r = CheckRealOrError(InputProp2, 5);
|
|
if (r) return r;
|
|
|
|
r = CheckRealOrError(InputProp3, 7);
|
|
if (r) return r;
|
|
|
|
// pass the arguments to the HumidAirProp.HAProps() function
|
|
Prop->real =
|
|
HumidAir::HAPropsSI(OutputName->str, InputName1->str, InputProp1->real, InputName2->str, InputProp2->real, InputName3->str, InputProp3->real);
|
|
|
|
// Note: HAPropsSI does not throw exceptions, but instead
|
|
// sets global parameter "errstring" and returns _HUGE.
|
|
// Use ValidNumber(val) to see if HAPropsSI failed with an error message...
|
|
if (!ValidNumber(Prop->real)) {
|
|
std::string emsg = CoolProp::get_global_param_string("errstring");
|
|
CoolProp::set_error_string(emsg); // reset error string so Mathcad can retrieve it
|
|
return HandleHAPropsSIError(emsg, OutName, Prop1Name, Prop2Name, Prop3Name);
|
|
}
|
|
|
|
// normal return
|
|
return 0;
|
|
}
|
|
|
|
// ********************************************************************************************************
|
|
// Pseudo-Low-Level Functions for mixture information & settings
|
|
// ********************************************************************************************************
|
|
|
|
// this code executes the user function CP_get_mixture_binary_pair_data, which is a wrapper for
|
|
// the CoolProp.get_mixture_binary_pair_data() function, used to get the requested binary pair
|
|
// interaction parameter (always returned as a string).
|
|
static LRESULT CP_get_mixture_binary_pair_data(LPMCSTRING Value, // output string (string contains value of parameter)
|
|
LPCMCSTRING CAS1, // First component
|
|
LPCMCSTRING CAS2, // Second component
|
|
LPCMCSTRING Key) // name of the binary pair parameter (string) to retrieve
|
|
{
|
|
std::string s;
|
|
// Invoke the std::string form of get_global_param_string() function, save result to a new string s
|
|
try {
|
|
s = CoolProp::get_mixture_binary_pair_data(CAS1->str, CAS2->str, Key->str);
|
|
} catch (const CoolProp::ValueError& e) {
|
|
std::string emsg(e.what());
|
|
CoolProp::set_error_string(emsg);
|
|
if (emsg.find("parameter") != std::string::npos)
|
|
return MAKELRESULT(BAD_PARAMETER, 3);
|
|
else if (emsg.find("binary pair") != std::string::npos)
|
|
return MAKELRESULT(BAD_BINARY_PAIR, 1);
|
|
else
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
}
|
|
|
|
// Must use MathcadAllocate(size) so Mathcad can track and release, using Helper routine above
|
|
char* c = AllocMathcadString(s);
|
|
// assign the string to the function's output parameter
|
|
Value->str = c;
|
|
|
|
// normal return
|
|
return 0;
|
|
}
|
|
|
|
// Helper: centralize check for predefined-mixture-not-found error messages
|
|
static inline bool PredefinedMixtureNotFound(const std::string& emsg) {
|
|
return (emsg.find("Predefined mixture") != std::string::npos && emsg.find("not found") != std::string::npos);
|
|
}
|
|
|
|
// Return semicolon-delimited fluid names for a predefined mixture
|
|
static LRESULT CP_get_predefined_mixture_fluids(LPMCSTRING Comps, // output string (semicolon-delimited fluid names)
|
|
LPCMCSTRING MixtureName) // name of predefined mixture (with or without .mix)
|
|
{
|
|
std::string s;
|
|
try {
|
|
std::string key = MixtureName->str;
|
|
if ((key.find(".mix") == std::string::npos) && (key.find(".MIX") == std::string::npos)) key += ".mix";
|
|
|
|
Dictionary dict;
|
|
if (!CoolProp::is_predefined_mixture(key, dict)) {
|
|
throw CoolProp::ValueError(format("Predefined mixture [%s] not found", key.c_str()));
|
|
}
|
|
|
|
const std::vector<std::string>& fluids = dict.get_string_vector("fluids");
|
|
s = strjoin(fluids, ";");
|
|
} catch (const CoolProp::ValueError& e) {
|
|
std::string emsg(e.what());
|
|
CoolProp::set_error_string(emsg);
|
|
if (PredefinedMixtureNotFound(emsg))
|
|
return MAKELRESULT(BAD_MIXTURE, 1);
|
|
else
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
} catch (...) {
|
|
CoolProp::set_error_string("Unknown error retrieving predefined mixture fluids");
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
}
|
|
|
|
char* c = AllocMathcadString(s);
|
|
Comps->str = c;
|
|
return 0;
|
|
}
|
|
|
|
// Return a column vector of mole fractions for a predefined mixture
|
|
static LRESULT CP_get_predefined_mixture_mole_fractions(LPCOMPLEXARRAY Dest, // output column vector of mole fractions
|
|
LPCMCSTRING MixtureName) // name of predefined mixture (with or without .mix)
|
|
{
|
|
std::vector<std::vector<double>> Vec;
|
|
try {
|
|
std::string key = MixtureName->str;
|
|
if ((key.find(".mix") == std::string::npos) && (key.find(".MIX") == std::string::npos)) key += ".mix";
|
|
|
|
Dictionary dict;
|
|
if (!CoolProp::is_predefined_mixture(key, dict)) {
|
|
throw CoolProp::ValueError(format("Predefined mixture [%s] not found", key.c_str()));
|
|
}
|
|
|
|
const std::vector<double>& mf = dict.get_double_vector("mole_fractions");
|
|
// Convert to vector-of-vectors with one column
|
|
Vec.reserve(mf.size());
|
|
for (std::size_t i = 0; i < mf.size(); ++i) {
|
|
Vec.push_back(std::vector<double>(1, mf[i]));
|
|
}
|
|
} catch (const CoolProp::ValueError& e) {
|
|
std::string emsg(e.what());
|
|
CoolProp::set_error_string(emsg);
|
|
if (PredefinedMixtureNotFound(emsg)) {
|
|
return MAKELRESULT(BAD_MIXTURE, 1);
|
|
}
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
} catch (...) {
|
|
std::string emsg("Unknown error retrieving predefined mixture mole fractions");
|
|
CoolProp::set_error_string(emsg);
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
}
|
|
|
|
if (Vec.empty()) {
|
|
// No mole fractions found -> return mixture error
|
|
std::string emsg("No mole fractions found for specified predefined mixture");
|
|
CoolProp::set_error_string(emsg);
|
|
return MAKELRESULT(BAD_MIXTURE, 1);
|
|
}
|
|
|
|
LRESULT rc = AllocateToMathcadArray(Dest, Vec);
|
|
if (rc) return rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// This code executes the user function CP_apply_simple_mixing_rule, which is a wrapper for
|
|
// the CoolProp.apply_simple_mixing_rule() function, used to set the mixing rule for a
|
|
// specific binary pair.
|
|
static LRESULT CP_apply_simple_mixing_rule(LPMCSTRING Msg, // output string (verification message)
|
|
LPCMCSTRING CAS1, // First component
|
|
LPCMCSTRING CAS2, // Second component
|
|
LPCMCSTRING Rule) // Mixing rule, either 'linear' or 'Lorentz-Berthelot'
|
|
{
|
|
std::string s = Rule->str;
|
|
s.append(" mixing rule set.");
|
|
// Invoke the std::string form of get_global_param_string() function, save result to a new string s
|
|
try {
|
|
CoolProp::apply_simple_mixing_rule(CAS1->str, CAS2->str, Rule->str);
|
|
} catch (const CoolProp::ValueError& e) {
|
|
std::string emsg(e.what());
|
|
CoolProp::set_error_string(emsg);
|
|
if (emsg.find("simple mixing rule") != std::string::npos) {
|
|
return MAKELRESULT(BAD_RULE, 3);
|
|
} else if (emsg.find("already in") != std::string::npos) {
|
|
return MAKELRESULT(PAIR_EXISTS, 1);
|
|
} else if (emsg.find("key") != std::string::npos) {
|
|
if (emsg.find(CAS1->str) != std::string::npos) {
|
|
return MAKELRESULT(BAD_FLUID, 1);
|
|
} else if (emsg.find(CAS2->str) != std::string::npos) {
|
|
return MAKELRESULT(BAD_FLUID, 2);
|
|
} else
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
} else
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
}
|
|
|
|
// Must use MathcadAllocate(size) so Mathcad can track and release
|
|
char* c = AllocMathcadString(s);
|
|
// assign the string to the function's output parameter
|
|
Msg->str = c;
|
|
|
|
// normal return
|
|
return 0;
|
|
}
|
|
|
|
// This code executes the user function CP_set_mixture_binary_pair_data, which is a wrapper for
|
|
// the CoolProp.set_mixture_binary_pair_data() function, used to set the mixing rule for a
|
|
// specific binary pair.
|
|
static LRESULT CP_set_mixture_binary_pair_data(LPMCSTRING Msg, // output string (verification message)
|
|
LPCMCSTRING CAS1, // First component
|
|
LPCMCSTRING CAS2, // Second component
|
|
LPCMCSTRING Param, // Parameter Name String to set
|
|
LPCCOMPLEXSCALAR Value) // Parameter Value
|
|
{
|
|
std::string s = Param->str;
|
|
s.append(" parameter set.");
|
|
|
|
// check that the first scalar argument is real
|
|
LRESULT r = CheckRealOrError(Value, 4);
|
|
if (r) return r;
|
|
|
|
// Invoke the std::string form of get_global_param_string() function, save result to a new string s
|
|
try {
|
|
CoolProp::set_mixture_binary_pair_data(CAS1->str, CAS2->str, Param->str, Value->real);
|
|
} catch (const CoolProp::ValueError& e) {
|
|
std::string emsg(e.what());
|
|
CoolProp::set_error_string(emsg);
|
|
if (emsg.find("parameter") != std::string::npos) {
|
|
return MAKELRESULT(BAD_PARAMETER, 3);
|
|
} else if (emsg.find("key") != std::string::npos) {
|
|
if (emsg.find(CAS1->str) != std::string::npos)
|
|
return MAKELRESULT(BAD_FLUID, 1);
|
|
else
|
|
return MAKELRESULT(BAD_FLUID, 2);
|
|
} else
|
|
return MAKELRESULT(UNKNOWN, 1);
|
|
}
|
|
|
|
// Must use MathcadAllocate(size) so Mathcad can track and release
|
|
char* c = AllocMathcadString(s);
|
|
// assign the string to the function's output parameter
|
|
Msg->str = c;
|
|
|
|
// normal return
|
|
return 0;
|
|
}
|
|
|
|
// ********************************************************************************************************
|
|
// Fill out a FUNCTIONINFO structure with the information needed for registering the function with Mathcad
|
|
// ********************************************************************************************************
|
|
|
|
FUNCTIONINFO PropsParam = {
|
|
"get_global_param_string", // Name by which Mathcad will recognize the function
|
|
"Name of the parameter to retrieve", // Description of input parameters
|
|
"Returns the value of the requested CoolProps parameter", // description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_get_global_param_string, // Pointer to the function code.
|
|
MC_STRING, // Returns a Mathcad string
|
|
1, // Number of arguments
|
|
{MC_STRING} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO FluidParam = {
|
|
"get_fluid_param_string", // Name by which Mathcad will recognize the function
|
|
"Fluid, Name of the parameter to retrieve", // Description of input parameters
|
|
"Returns the value of the requested CoolProps parameter", // description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_get_fluid_param_string, // Pointer to the function code.
|
|
MC_STRING, // Returns a Mathcad string
|
|
2, // Number of arguments
|
|
{MC_STRING, MC_STRING} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO RefState = {
|
|
"set_reference_state", // Name by which Mathcad will recognize the function
|
|
"Fluid, Reference State String", // Description of input parameters
|
|
"Sets the reference state to either IIR, ASHRAE, NBP, or DEF.", // description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_set_reference_state, // Pointer to the function code.
|
|
COMPLEX_SCALAR, // Returns a Mathcad complex scalar
|
|
2, // Number of arguments
|
|
{MC_STRING, MC_STRING} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO Props1SI = {
|
|
"Props1SI", // Name by which Mathcad will recognize the function
|
|
"Fluid, Property Name", // Description of input parameters
|
|
"Returns a fluid-specific parameter, where the parameter is not dependent on the fluid state", // Description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_Props1SI, // Pointer to the function code.
|
|
COMPLEX_SCALAR, // Returns a Mathcad complex scalar
|
|
2, // Number of arguments
|
|
{MC_STRING, MC_STRING} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO PropsSI = {
|
|
"PropsSI", // Name by which Mathcad will recognize the function
|
|
"Output Name, Input Name 1, Input Property 1, Input Name 2, Input Property 2, Fluid Name", // Description of input parameters
|
|
"Returns a fluid-specific parameter, where the parameter is dependent on the fluid state", // Description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_PropsSI, // Pointer to the function code.
|
|
COMPLEX_SCALAR, // Returns a Mathcad complex scalar
|
|
6, // Number of arguments
|
|
{MC_STRING, MC_STRING, COMPLEX_SCALAR, MC_STRING, COMPLEX_SCALAR, MC_STRING} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO PropsSImulti = {
|
|
"PropsSImulti", // // Name by which Mathcad will recognize the function
|
|
"Output Name, Input Name 1, Input Property 1 (Array), Input Name 2, Input Property 2 (Array), Fluid Name", // Description of input parameters
|
|
"Returns a range of fluid-specific parameters, where the parameters are dependent on the state ranges defined by the input property arrays", // Description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_PropsSImulti, // Pointer to the function code.
|
|
COMPLEX_ARRAY, // Returns a Mathcad complex array
|
|
6, // Number of arguments
|
|
{MC_STRING, MC_STRING, COMPLEX_ARRAY, MC_STRING, COMPLEX_ARRAY, MC_STRING} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO PhaseSI = {
|
|
"PhaseSI", // Name by which Mathcad will recognize the function
|
|
"Input Name 1, Input Property 1, Input Name 2, Input Property 2, Fluid Name", // Description of input parameters
|
|
"Returns the fluid phase, dependent on the fluid state", // Description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_PhaseSI, // Pointer to the function code.
|
|
MC_STRING, // Returns a Mathcad String
|
|
5, // Number of arguments
|
|
{MC_STRING, COMPLEX_SCALAR, MC_STRING, COMPLEX_SCALAR, MC_STRING} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO HAPropsSI = {
|
|
"HAPropsSI", // Name by which Mathcad will recognize the function
|
|
"Output Name, Input Name 1, Input Property 1, Input Name 2, Input Property 2, Input Name 3, Input Property 3", // Description of input parameters
|
|
"Returns a parameter of humid air, where the parameter is dependent on the fluid state", // Description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_HAPropsSI, // Pointer to the function code.
|
|
COMPLEX_SCALAR, // Returns a Mathcad complex scalar
|
|
7, // Number of arguments
|
|
{MC_STRING, MC_STRING, COMPLEX_SCALAR, MC_STRING, COMPLEX_SCALAR, MC_STRING, COMPLEX_SCALAR} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO GetMixtureData = {
|
|
"get_mixture_binary_pair_data", // Name by which Mathcad will recognize the function
|
|
"CAS 1, CAS 2, Name of the parameter to retrieve", // Description of input parameters
|
|
"Returns the value of the requested CoolProps parameter", // description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_get_mixture_binary_pair_data, // Pointer to the function code.
|
|
MC_STRING, // Returns a Mathcad string
|
|
3, // Number of arguments
|
|
{MC_STRING, MC_STRING, MC_STRING} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO ApplyMixingRule = {
|
|
"apply_simple_mixing_rule", // Name by which Mathcad will recognize the function
|
|
"CAS 1, CAS 2, Mixing Rule", // Description of input parameters
|
|
"Sets a simple mixing rule for binary pair", // description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_apply_simple_mixing_rule, // Pointer to the function code.
|
|
MC_STRING, // Returns a Mathcad string
|
|
3, // Number of arguments
|
|
{MC_STRING, MC_STRING, MC_STRING} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO SetMixtureData = {
|
|
"set_mixture_binary_pair_data", // Name by which Mathcad will recognize the function
|
|
"CAS 1, CAS 2, Parameter Name, Parameter value", // Description of input parameters
|
|
"Sets the value of the specified binary mixing parameter", // description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_set_mixture_binary_pair_data, // Pointer to the function code.
|
|
MC_STRING, // Returns a Mathcad string
|
|
4, // Number of arguments
|
|
{MC_STRING, MC_STRING, MC_STRING, COMPLEX_SCALAR} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO GetPredefFluids = {
|
|
"get_predefined_mixture_fluids", // Name by which Mathcad will recognize the function
|
|
"MixtureName", // Description of input parameters
|
|
"Returns semicolon-delimited fluid names in the predefined mixture", // description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_get_predefined_mixture_fluids, // Pointer to the function code.
|
|
MC_STRING, // Returns a Mathcad string
|
|
1, // Number of arguments
|
|
{MC_STRING} // Argument types
|
|
};
|
|
|
|
FUNCTIONINFO GetPredefMoleFracs = {
|
|
"get_predefined_mixture_fractions", // Name by which Mathcad will recognize the function
|
|
"MixtureName", // Description of input parameters
|
|
"Returns a column vector of mole fractions for the predefined mixture", // description of the function for the Insert Function dialog box
|
|
(LPCFUNCTION)CP_get_predefined_mixture_mole_fractions, // Pointer to the function code.
|
|
COMPLEX_ARRAY, // Returns a Mathcad complex array
|
|
1, // Number of arguments
|
|
{MC_STRING} // Argument types
|
|
};
|
|
|
|
// ************************************************************************************
|
|
// DLL entry point code.
|
|
// ************************************************************************************
|
|
// Note 1: The _CRT_INIT function is needed if using Microsoft's WIN32 library and you want to use the C runtime library,
|
|
// which we do for string handling and other utilities. If you are using a different compiler or setup,
|
|
// this may not be needed. It is only defined here if _WIN32 is defined.
|
|
// Note 2: The the _CRT_INIT function is not called directly in our code, but is instead called by the DllEntryPoint function
|
|
// Note 3: MS IntelliSense will warn "VCR001 Definition for _CRT_INIT not found", but this warning can be safely
|
|
// ignored as a known false positive. To disable, got to Tools > Options > Text Editor > C/C++ > IntelliSense and
|
|
// set "Create declaration/definition suggestion level" to "Refactoring only".
|
|
#ifdef _WIN32
|
|
extern "C" BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved);
|
|
#endif
|
|
|
|
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) {
|
|
switch (dwReason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
//
|
|
// DLL is attaching to the address space of the current process.
|
|
//
|
|
if (!_CRT_INIT(hDLL, dwReason, lpReserved)) return FALSE;
|
|
|
|
// Register the error message table
|
|
if (!CreateUserErrorMessageTable(hDLL, NUMBER_OF_ERRORS, CPErrorMessageTable)) break;
|
|
|
|
// ...and if the errors register OK, go ahead and register user function
|
|
CreateUserFunction(hDLL, &PropsParam);
|
|
CreateUserFunction(hDLL, &FluidParam);
|
|
CreateUserFunction(hDLL, &RefState);
|
|
CreateUserFunction(hDLL, &Props1SI);
|
|
CreateUserFunction(hDLL, &PropsSI);
|
|
CreateUserFunction(hDLL, &PropsSImulti);
|
|
CreateUserFunction(hDLL, &PhaseSI);
|
|
CreateUserFunction(hDLL, &HAPropsSI);
|
|
CreateUserFunction(hDLL, &GetMixtureData);
|
|
CreateUserFunction(hDLL, &SetMixtureData);
|
|
CreateUserFunction(hDLL, &ApplyMixingRule);
|
|
// Register the new helper functions for predefined mixtures
|
|
CreateUserFunction(hDLL, &GetPredefFluids);
|
|
CreateUserFunction(hDLL, &GetPredefMoleFracs);
|
|
break;
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
case DLL_THREAD_DETACH:
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
if (!_CRT_INIT(hDLL, dwReason, lpReserved)) {
|
|
Sleep(1000); // Attempt to keep CRT_INIT from detaching before all threads are closed
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|