You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmCalculation/nmCalculationUtils.cpp

448 lines
13 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmCalculationUtils.h"
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QCoreApplication>
#include <QMutex>
#include <QMutexLocker>
#include <fstream>
#include <float.h>
#include "pch.h"
namespace
{
// HX_NWTM.dll 的配置和结果查询接口使用进程级共享状态,所有入口共用此锁。
QMutex s_oHxNwtmDllMutex;
bool isKrigingCancellationRequested(const QAtomicInt* pCancelRequested)
{
return pCancelRequested != NULL &&
static_cast<int>(*pCancelRequested) != 0;
}
class nmKrigingMutexLocker
{
public:
nmKrigingMutexLocker()
: m_pMutex(NULL),
m_bLocked(false)
{
}
~nmKrigingMutexLocker()
{
if(m_bLocked && m_pMutex != NULL) {
m_pMutex->unlock();
}
}
bool lock(QMutex* pMutex, const QAtomicInt* pCancelRequested)
{
if(pMutex == NULL) {
return false;
}
if(pCancelRequested == NULL) {
pMutex->lock();
} else {
// 只改变等待方式,不改变 DLL 全局锁的保护范围和互斥语义。
while(!pMutex->tryLock(100)) {
if(isKrigingCancellationRequested(pCancelRequested)) {
return false;
}
}
}
m_pMutex = pMutex;
m_bLocked = true;
return true;
}
private:
QMutex* m_pMutex;
bool m_bLocked;
};
QString krigingText(const char* sourceText)
{
return QCoreApplication::translate("nmCalculationUtils", sourceText);
}
bool isFiniteValue(double value)
{
#ifdef _MSC_VER
return _finite(value) != 0;
#else
return std::isfinite(value);
#endif
}
void setKrigingError(QString* errorMessage, const QString& message)
{
if(errorMessage != NULL) {
*errorMessage = message;
}
}
}
nmCalculationUtils::nmCalculationUtils() {
}
QMutex* nmCalculationUtils::getHxNwtmDllMutex()
{
return &s_oHxNwtmDllMutex;
}
double nmCalculationUtils::milliDarcyToDarcy(double dPermeabilityMilliDarcy)
{
// PEBI输入结构使用D数据层统一使用mD因此仅在求解器边界换算。
return dPermeabilityMilliDarcy / 1000.0;
}
bool nmCalculationUtils::copyFileToDir(QString filePath, QString destDir) {
// 如果文件不存在,则退出
if(!QFile::exists(filePath)) {
return false;
}
// 如果目标目录不存在,则创建
if(!QDir(destDir).exists()) {
if(!QDir().mkdir(destDir)) {
qWarning() << "cannot mkdir:" << destDir;
return false;
}
}
// 获取源文件的文件名
QString fileName = QFileInfo(filePath).fileName();
// 构造目标文件的完整路径
QString destinationFilePath = QDir(destDir).filePath(fileName);
// 如果目标文件已存在,则删除
if(QFile::exists(destinationFilePath)) {
QFile destinationFile(destinationFilePath);
if(!destinationFile.remove()) {
qWarning() << "cannot delete file:" << destinationFilePath;
return false;
}
}
// 拷贝文件
if(QFile::copy(filePath, destinationFilePath)) {
qDebug() << "file copy success:" << destinationFilePath;
return true;
} else {
qWarning() << "file copy failed:" << destinationFilePath;
return false;
}
return true;
}
bool nmCalculationUtils::createDirectoryIfNotExists(QString directoryPath) {
QDir dir(directoryPath);
// 检查目录是否存在
if(dir.exists()) {
// 目录已存在
return true;
}
// 尝试创建目录
if(dir.mkpath(directoryPath)) {
// 创建成功
return true;
}
// 创建失败
qDebug() << "创建目录失败,错误代码。";
return false;
}
bool nmCalculationUtils::copyFile(const std::string& sourcePath, const std::string& destinationPath) {
std::ifstream src(sourcePath, std::ios::binary); // 以二进制模式打开源文件
std::ofstream dst(destinationPath, std::ios::binary); // 以二进制模式打开目标文件
if(!src || !dst) {
std::cerr << "文件打开失败" << std::endl;
return false;
}
dst << src.rdbuf(); // 拷贝文件内容
return true;
}
bool nmCalculationUtils::removeDirectory(const QString &dirPath) {
QDir dir(dirPath);
if(!dir.exists()) {
qDebug() << "Directory does not exist.";
return true;
}
// 获取目录中的所有文件和目录
QFileInfoList fileList = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden);
foreach(QFileInfo fileInfo, fileList) {
if(fileInfo.isDir()) {
// 递归删除子目录
if(!removeDirectory(fileInfo.absoluteFilePath())) {
return false;
}
} else {
// 删除文件
if(!QFile::remove(fileInfo.absoluteFilePath())) {
qDebug() << "Failed to remove file:" << fileInfo.absoluteFilePath();
return false;
}
}
}
// 删除目录自身
if(!dir.rmdir(dirPath)) {
qDebug() << "Failed to remove directory:" << dirPath;
return false;
}
return true;
}
bool nmCalculationUtils::writeFile(const QStringList& content, const QString &filePath) {
QFile file(filePath);
//检查
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning("Cannot open file for writing: %s", qPrintable(file.errorString()));
return false;
}
QTextStream out(&file);
out << content.join("\n");
file.close();
return true;
}
QStringList nmCalculationUtils::readFile(const QString &filePath) {
QStringList content;
// 打开文件
QFile file(filePath);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "Failed to open file:" << filePath;
return content;
}
// 使用QTextStream读取文件
QTextStream in(&file);
while(!in.atEnd()) {
// 读取一行
QString line = in.readLine();
// 将行内容放入QStringList
content.append(line);
}
// 关闭文件
file.close();
return content;
}
bool nmCalculationUtils::calculateKriging(
const QVector<QPointF>& targetPoints,
const QVector<QPointF>& measurementPoints,
const QVector<double>& measurementValues,
double nugget,
double sill,
double range,
int model,
const QString& licensePath,
QVector<double>& outputValues,
QString* errorMessage,
const QAtomicInt* pCancelRequested)
{
outputValues.clear();
setKrigingError(errorMessage, QString());
if(isKrigingCancellationRequested(pCancelRequested)) {
setKrigingError(errorMessage, krigingText("Kriging calculation was cancelled."));
return false;
}
if(targetPoints.isEmpty()) {
setKrigingError(errorMessage, krigingText("No interpolation points are available."));
return false;
}
if(measurementPoints.size() < 2) {
setKrigingError(errorMessage,
krigingText("At least two measurement points are required."));
return false;
}
if(measurementPoints.size() != measurementValues.size()) {
setKrigingError(errorMessage,
krigingText("Measurement point coordinates and values do not match."));
return false;
}
if(!isFiniteValue(nugget) || !isFiniteValue(sill) ||
!isFiniteValue(range) || nugget < 0.0 || sill <= 0.0 || range <= 0.0) {
setKrigingError(errorMessage, krigingText("Kriging parameters are invalid."));
return false;
}
if(model < 0 || model > 2) {
setKrigingError(errorMessage, krigingText("The Kriging model is invalid."));
return false;
}
for(int i = 0; i < targetPoints.size(); ++i) {
if((i % 256) == 0 &&
isKrigingCancellationRequested(pCancelRequested)) {
setKrigingError(errorMessage, krigingText("Kriging calculation was cancelled."));
return false;
}
if(!isFiniteValue(targetPoints[i].x()) || !isFiniteValue(targetPoints[i].y())) {
setKrigingError(errorMessage,
krigingText("An interpolation point contains an invalid coordinate."));
return false;
}
}
for(int i = 0; i < measurementPoints.size(); ++i) {
if(isKrigingCancellationRequested(pCancelRequested)) {
setKrigingError(errorMessage, krigingText("Kriging calculation was cancelled."));
return false;
}
if(!isFiniteValue(measurementPoints[i].x()) ||
!isFiniteValue(measurementPoints[i].y()) ||
!isFiniteValue(measurementValues[i])) {
setKrigingError(errorMessage,
krigingText("A measurement point contains invalid data."));
return false;
}
for(int j = 0; j < i; ++j) {
if(measurementPoints[i] == measurementPoints[j]) {
setKrigingError(errorMessage,
krigingText("Measurement point coordinates cannot be duplicated."));
return false;
}
}
}
if(licensePath.isEmpty() || !QFileInfo(licensePath).exists()) {
setKrigingError(errorMessage, krigingText("The solver license file was not found."));
return false;
}
// Kriging 与建网、模型求解来自同一个 DLL不能在不同线程中并发进入。
nmKrigingMutexLocker oDllLocker;
if(!oDllLocker.lock(getHxNwtmDllMutex(), pCancelRequested)) {
setKrigingError(errorMessage, krigingText("Kriging calculation was cancelled."));
return false;
}
HMODULE dll = LoadLibrary(L"HX_NWTM.dll");
if(dll == NULL) {
setKrigingError(errorMessage, krigingText("Failed to load HX_NWTM.dll."));
return false;
}
typedef void (*HX_NWTM_KRINGING_Func)(HX_KRING_OUTPUT&,
const HX_KRING_INPUT,
std::string);
HX_NWTM_KRINGING_Func krigingFunction =
(HX_NWTM_KRINGING_Func)GetProcAddress(dll, "HX_NWTM_KRINGING");
if(krigingFunction == NULL) {
FreeLibrary(dll);
setKrigingError(errorMessage,
krigingText("The Kriging interface was not found in HX_NWTM.dll."));
return false;
}
// 求解器p参数每行保存{x, y}并保持targetPoints的原始顺序.
dVec2 targetData;
targetData.reserve(targetPoints.size());
for(int i = 0; i < targetPoints.size(); ++i) {
dVec1 point(2, 0.0);
point[0] = targetPoints[i].x();
point[1] = targetPoints[i].y();
targetData.push_back(point);
}
// 求解器v参数每行保存{x, y, value},坐标和值按相同下标配对.
dVec2 measurementData;
measurementData.reserve(measurementPoints.size());
for(int i = 0; i < measurementPoints.size(); ++i) {
dVec1 point(3, 0.0);
point[0] = measurementPoints[i].x();
point[1] = measurementPoints[i].y();
point[2] = measurementValues[i];
measurementData.push_back(point);
}
bool calculationSucceeded = true;
QString calculationError;
// 局部作用域保证求解器输入、输出对象在FreeLibrary前析构.
{
HX_KRING_INPUT input(nugget, sill, range, model,
targetData, measurementData);
HX_KRING_OUTPUT output;
try {
krigingFunction(output, input, licensePath.toStdString());
} catch(const std::exception& exception) {
calculationSucceeded = false;
calculationError = krigingText("Kriging calculation failed: %1")
.arg(QString::fromLocal8Bit(exception.what()));
} catch(...) {
calculationSucceeded = false;
calculationError = krigingText("Kriging calculation failed.");
}
// DLL 无取消入口;调用期间收到停止请求时只丢弃返回值,不提交插值结果。
if(calculationSucceeded &&
isKrigingCancellationRequested(pCancelRequested)) {
calculationSucceeded = false;
calculationError = krigingText("Kriging calculation was cancelled.");
}
if(calculationSucceeded &&
output.v.size() != static_cast<size_t>(targetPoints.size())) {
calculationSucceeded = false;
calculationError = krigingText(
"The Kriging result count does not match the interpolation points.");
}
if(calculationSucceeded) {
outputValues.reserve(targetPoints.size());
for(size_t i = 0; i < output.v.size(); ++i) {
if((i % 256) == 0 &&
isKrigingCancellationRequested(pCancelRequested)) {
calculationSucceeded = false;
calculationError = krigingText("Kriging calculation was cancelled.");
break;
}
if(!isFiniteValue(output.v[i])) {
calculationSucceeded = false;
calculationError = krigingText(
"The Kriging result contains an invalid value.");
break;
}
outputValues.append(output.v[i]);
}
}
}
FreeLibrary(dll);
if(!calculationSucceeded) {
outputValues.clear();
setKrigingError(errorMessage, calculationError);
}
return calculationSucceeded;
}