title: 单元模块开发入门实例Heater(一)
top_img: false
tags:
  - CAPE-OPEN
categories:
  - 化工流程模拟开发
cover: '/upload/cdn5files/202401292022446.png'
abbrlink: 5e4b42e2
date: 2024-01-29 20:18:04
copyright:
comments:

20241117更新

建议直接看《单元模块开发入门实例Heater(二)》
本文属于历史遗留问题,还没解决。

前言

那么我们在单元模块开发入门篇中草草的就结束了计算实现部分,当然是因为我技术不行,还没吃透,而且实际上的单元模块双击之后的编辑界面也没有实现

本篇就是单元模块开发入门篇的一个番外篇,将直接从一个换热器模块实例来入手

建议不要直接从本篇开始,循序渐进从物性包开发再到单元模块开发入门,再到本篇

这样有一个简单的理解过程,并且本篇的代码是基于单元模块开发篇的,并不会直接从零开始

不用怕,本文所有的代码直接开源,文章最后我会放上项目源码地址!

获取物流对象部分

在单元模块开发入门篇3的基础上

首先来修改一下 BlockPort.h 中我们获取物流对象的方式:

从直接使用热力学接口更改为智能指针

更改端口名字和描述的转换方式:

这里更改为了wstring类型,继续更改下方获取名字和描述的方法:

其余部分不变

调用物流对象部分

回到 MyBlockPortsArray.h 文件中

完善进出口流股的调用,其余部分不变

注意调用是使用指针返回函数

函数封装

在物性包开发入门篇中我们讲过,在本篇的获取物流对象部分也可以看到,获取物流对象使用的是 ICapeThermoMaterial 接口,那么在计算调用的时候,想要获取到物流对象中的参数,就使用的是该接口的 GetOverallProp() 函数,那么在标准文档中,对该函数的定义是这样的:

三个参数分别是参数名称、参数单位以及值,而且也给出了参数的类型,那么我们获取的话就可以这样写:

GetOverallProp(CBSTR(_T("temperature")), NULL, &myValue.value);	// 获取温度

NULL 表示使用模拟软件的默认温度, myValue 是一个临时存放值的变量

那么我们接下来就写获取各个参数的方法

首先我们要解决一个CAPE-OPEN遗留的问题,就是在 Variant.h 文件中对于 VARIANT value 的定义是类的私有成员,会导致我们在获取value的时候访问不到值的情况,来到 Variant.h 文件中:

接着就来到 MyBlock.h 文件中,进行参数的获取和计算

在单元模块开发入门篇3中我们简单的实现过一次所谓的计算:

可以看到是比较复杂的,那么对于每一个参数,每一个流股都要写这么一长串,代码是非常冗余且不规范的

实际上获取参数的步骤都是统一的,那么我们可以把获取参数的方法封装成一个我们自己的函数,在需要获取参数的时候直接进行调用就行了,这样就会简单和规范很多

首先是获取入口流股参数的函数:

// 获取进口流股物流对象中的参数,主要为温度、压力、摩尔流量、摩尔组成
BOOL GetOverallTPFlowComposition(double& temperature, double& pressure, double& totalMoleFlow, CVariant& moleComposition)
{
	// 定义临时变量
	HRESULT hr;
	std::wstring error;
	CVariant myValue;
	// 获取温度
	hr = myPortArray->getInlet()->GetOverallProp(CBSTR(_T("temperature")), NULL, &myValue.value);
	myValue.CheckArray(VT_R8, error);
	temperature = myValue.GetDoubleAt(0);
	// 获取压力
	hr = myPortArray->getInlet()->GetOverallProp(CBSTR(_T("pressure")), NULL, &myValue.value);
	!myValue.CheckArray(VT_R8, error);
	pressure = myValue.GetDoubleAt(0);
	// 获取总摩尔流量
	hr = myPortArray->getInlet()->GetOverallProp(CBSTR(_T("totalFlow")), CBSTR(_T("mole")), &myValue.value);
	!myValue.CheckArray(VT_R8, error);
	totalMoleFlow = myValue.GetDoubleAt(0);
	// 获取组分的摩尔分率
	VARIANT v;
	v.vt = VT_EMPTY;
	hr = myPortArray->getInlet()->GetOverallProp(CBSTR(_T("fraction")), CBSTR(_T("mole")), &v);
	myValue.CheckArray(VT_R8, error);
	moleComposition.Set(v, true);

	return 1;
}

那么既然有了获取流股参数的函数,同样,我们给出口流股赋值的时候也是一样的,也可以封装成一个函数:

// 将计算完毕的参数赋值给流股并执行一次闪蒸
BOOL SetOverallTPFlowCompositionAndFlash(double temperature, double pressure, double totalMoleFlow, CVariant& moleComposition)
{
	// 定义临时变量
	HRESULT hr;
	CVariant myValue;
	// 设置温度
	myValue.MakeArray(1, VT_R8);
	myValue.SetDoubleAt(0, temperature);
	hr = myPortArray->getOutlet()->SetOverallProp(CBSTR(L"temperature"), NULL, myValue);
	// 设置压力
	myValue.MakeArray(1, VT_R8);
	myValue.SetDoubleAt(0, pressure);
	hr = myPortArray->getOutlet()->SetOverallProp(CBSTR(L"pressure"), NULL, myValue);
	// 设置总摩尔流量
	myValue.MakeArray(1, VT_R8);
	myValue.SetDoubleAt(0, totalMoleFlow);
	hr = myPortArray->getOutlet()->SetOverallProp(CBSTR(L"totalFlow"), CBSTR(L"mole"), myValue);
	// 设置组分摩尔分率
	hr = myPortArray->getOutlet()->SetOverallProp(CBSTR(L"fraction"), CBSTR(L"mole"), moleComposition);
	// 执行一次闪蒸,确定出口流股的相态
	CalcEquilibriumByTemperatureAndPressure();

	return 1;
}

这里可以看到有不一样的地方,就是在设置出口流股参数的时候我们进行了一次闪蒸,那么闪蒸的目的是什么呢,就是必须要确定出口流股的相态,这是模拟软件中所规定的

这个闪蒸的函数如下:

// 闪蒸函数
BOOL CalcEquilibriumByTemperatureAndPressure()
{
	// 定义临时变量
	CVariant flashSpec1, flashSpec2;
	CBSTR overall(L"overall");
	// 获取温度
	flashSpec1.MakeArray(3, VT_BSTR);
	flashSpec1.AllocStringAt(0, L"temperature");
	flashSpec1.SetStringAt(1, NULL);
	flashSpec1.SetStringAt(2, overall);
	// 获取压力
	flashSpec2.MakeArray(3, VT_BSTR);
	flashSpec2.AllocStringAt(0, L"pressure");
	flashSpec2.SetStringAt(1, NULL);
	flashSpec2.SetStringAt(2, overall);
	// 创建一个闪蒸计算的实例
	CComPtr<ICapeThermoEquilibriumRoutine> capeThermoEquilibriumRoutine;
	// 获取赋值完毕的出口流股信息
	myPortArray->getOutlet()->QueryInterface(IID_ICapeThermoEquilibriumRoutine, (LPVOID*)&capeThermoEquilibriumRoutine);
	// 执行闪蒸
	HRESULT hr = capeThermoEquilibriumRoutine->CalcEquilibrium(flashSpec1, flashSpec2, CBSTR(_T("unspecified")));

	return 1;
}

将这三个函数依次定义在 Calculate() 函数之前即可

实现计算

Calculate() 函数内,直接调用上述我们封装好的函数:

现在基本上已经实现了一个将进口参数赋值给出口参数的一个什么都没有计算的模块

但是实际上这个模块bug是很多的,比如在Aspen中运行会报错,在COFE中运行虽然可以得到结果,但是依然会有警告,没有输入参数的窗口等

没关系,我们慢慢来完善

我们先简单的直接给定一个参数来试试计算结果:

也就是说,我们获取了进口的参数之后,让出口在300K,201325Pa下进行闪蒸,这就是一个简单的Heater模块

编译一下,编译通过,但是COFE测试发现还是有bug

那么回到 BlockPort.h 文件中:

MyBlockPortsArray.h 中:

编译一下,在Aspen中还是无法计算,在COFE中虽然可以计算并且得出结果如下图,但是一旦查看结果,第二次查看结果的时候,结果就直接为空了,说明还是有bug

这个时候点击下方的这些详细结果是有结果的,但是再点击一次就会闪退,还是有bug啊,头好疼

先更新到这里,我去想想办法

暂时停更

因为物流对象设置失败这个bug迟迟解决不了,暂时停更一段时间吧,源码地址放在下面了,希望有大佬能解答,联系方式Q:1395404299

源码地址:https://github.com/laugh0608/myBlockTest

现在的主要问题有两个:

第一就是在获取物流对象的参数时,value已在Variant.h文件中注册私有成员无法访问

第二就是在端口获取连接对象时,一旦加上 (*connectedObject)->AddRef(); 计数语句就会直接闪退

很头疼,还是没吃透

20240203更新

第一个问题已经解决,在 Variant.h 中定义:

MyBlock.h 中调用:

源码已更新上传

第二个bug,静待有缘人大佬能给我解答,我现在也去看看cape文档,研究研究

20241103更新

环境:VS2022、Win11、64位debug模式

MFC环境:AspenPlusV11

测试条件:

组分:水;进口温度:25C;进口压力:1.1bar

测试结果:无报错无警告;截图如下:

20241103204354.png
20241103204440.png
20241103204805.png

COFE无法运行,猜测是获取流股对象时使用了智能指针而非热力学对象,还在完善中

补充说明: 由于新的代码是后来我直接全部推翻重新写的,所以部分变量名称和文件名称不太一样,不过99%的代码都是一样的,可以自己研究一下改了哪些地方

最新源码地址:https://github.com/laugh0608/BlockTest01