基于 Fortran QuickWin 的物性计算应用程序开发示例


简介

学习Fortran QuickWin应用程序开发,编写了一个带有用户界面的简单的气体物性计算软件,可以称为超级低配版NIST Refprop(功能上比Refprop简单很多:只有氮气一种气体;没有添加绘制曲线的功能)

下面主要介绍QuickWin部分的内容,有关计算的例程和模块由我的小伙伴编写,使用了理想气体状态方程、van der Waals方程和R-K方程,迭代算法采用最简单的Newton迭代法。

With my partner: (1105808579@qq.com)


一、开发环境

IDE: Microsoft Visual Studio 2019 Community
编译器: Intel Parallel Studio XE 2020
源代码格式:Fortran 90

二、步骤

2.1 建立QuickWin工程

在VS中创建新项目,语言为Fortran,选择QuickWin Application
(这里命名为Ex01.slnEx01.vfproj )

在这里插入图片描述

2.2 添加源代码和文件

在解决方案资源管理器的Source Files目录下添加源代码文件
Source1.f90

在这里插入图片描述

2.3 添加资源文件

在Resource Files目录下新建资源文件 Resource1.rc
步骤同上

2.4 向 .rc 文件中添加资源

双击打开 .RC 文件,右键“添加资源”,然后选择对话框组件(Dialog)

在这里插入图片描述
除对话框外,还可以向 .rc 文件中添加菜单Menu、版本信息Version、位图Bitmap等;
要在对话框组件中插入位图,就需要先向 .rc 文件添加bitmap资源。
添加并配置Version资源,即可设置最终exe的属性-详细信息。

2.5 设计对话框

添加Dialog组件后,进入对话框的编辑界面

Ctrl + Alt + X 打开工具箱,可以将工具箱中的控件直接拖动到对话框里面
F4 打开属性窗口

在这里插入图片描述
(上图仅为简单示例)

在这里添加对话框中的按钮、文本框、滚动条等控件,通过工具栏中的工具调整控件的大小、位置和对齐方式,通过属性窗口设置控件显示文本(Caption)、控件ID、边缘粗细、可视性、文本对齐方式等属性。

控件可大致分为两类,一类是有触发回调例程(Callback Subroutine)的,例如按钮、EDIT文本框,另一类则是不能触发回调例程的,如 Static Text 和 Picture Control.

2.6 获取控件ID

每一个控件都有一个唯一ID作为其身份标识,ID和控件是一一对应的。例如,添加的第一个 Static Text 类型控件的ID是IDC_STATIC1,对话框1的控件ID是IDD_DIALOG1。然而这种形式的控件ID无法在程序中被直接识别。因为在保存了 .rc 文件后,每一个ID也被赋予一个与其对应的代码,如本例中IDD_DIALOG1的代码为101IDC_EDIT1的代码为1000. 在程序中引用控件需要通过控件代码,因此,保存 rc 文件之后,首先打开工程目录下的 resource.h 文件,这是系统生成的一个C++文件,里面包含着控件ID与控件身份标识代码的对应关系。

在这里插入图片描述

在这里插入图片描述

人为读取之后,用模块的形式写入f90源文件中
(后面需要访问控件的例程都必须包含此模块)

MODULE CONTROLS
	integer,parameter::IDD_DIALOG1 = 101
	integer,parameter::IDD_EDIT1 = 1000
	integer,parameter::IDD_EDIT2 = 1001
	integer,parameter::IDD_EDIT3 = 1002
END MODULE CONTROLS

P.S. 本来应该用命令行将resource.h转化为Fortran能够识别的.fd文件然后直接将其包含在程序中,但多次尝试都无法实现,此时只能手动包含控件信息。

2.7 编写主程序和回调例程

这里附上氮气物性计算软件的源代码:

MODULE CONTROLS	!resource.h中的控件ID
	integer, parameter :: IDD_DIALOG1 = 101
	integer, parameter :: IDD_DIALOG2 = 103
	integer, parameter :: IDC_EDIT1 = 1000
	integer, parameter :: IDC_EDIT2 = 1001
	integer, parameter :: IDC_EDIT3 = 1002
	integer, parameter :: IDC_STATIC1 = 1003
	integer, parameter :: IDC_STATIC2 = 1004
	integer, parameter :: IDC_STATIC3 = 1005
	integer, parameter :: IDC_STATIC4 = 1006 
END MODULE CONTROLS
	

MODULE NEWTON	!内核算法模块,包含方程
    IMPLICIT NONE
    REAL,PARAMETER::R=8.314
    REAL,PARAMETER::MM=0.028013 !氮气摩尔质量
    REAL,PARAMETER::A=0.1361,B=0.0385*0.001 !范德瓦尔方程正系数
    REAL,PARAMETER::AK=0.427480*R**2*126.2**2.5/3.39/(1E6),BK=0.08664*R*126.2/3.39/(1E6) !R-K方程正系数
    REAL::ML,VM,X
	
    CONTAINS    
	!范德瓦尔方程求解 
    REAL FUNCTION VANSOLVE(P,V,T)
		IMPLICIT NONE
		REAL::P,V,T,X
		
		ML=P*V/(T*R/MM) !理想气体状态方程求解
		X=V/(ML/MM)
		VM=X-VANDER(P,V,T,X)/DVANDER(P,V,T,X)
		DO WHILE(ABS(X-VM)>1E-7)
			X=VM
			VM=X-VANDER(P,V,T,X)/DVANDER(P,V,T,X)
		END DO
		VANSOLVE=V/VM*MM
	END FUNCTION VANSOLVE
	
    REAL FUNCTION VANDER(P,V,T,X) !范德瓦尔函数
		IMPLICIT NONE
		REAL P,V,T,X
		VANDER=P*X**3-(P*B+R*T)*X**2+A*X-A*B
	END FUNCTION VANDER
	
    REAL FUNCTION DVANDER(P,V,T,X) !范德瓦尔导函数
		IMPLICIT NONE
		REAL::P,V,T,X
		DVANDER=3*P*X**2-2*(P*B+R*T)*X+A
    END FUNCTION DVANDER

	!R-K方程求解
    REAL FUNCTION RKSOLVE(P,V,T)
		IMPLICIT NONE
		REAL::P,V,T
		ML=P*V/(T*R/MM) !理想气体状态方程求解
		X=V/(ML/MM)
		VM=X-RK(P,V,T,X)/DRK(P,V,T,X)
		DO WHILE(ABS(X-VM)>1E-7)
			X=VM
			VM=X-RK(P,V,T,X)/DRK(P,V,T,X)
		END DO
		RKSOLVE=V/VM*MM
	END FUNCTION RKSOLVE
	
    REAL FUNCTION RK(P,V,T,X) !R-K函数
		IMPLICIT NONE
		REAL::P,V,T,X
		RK=P*X**3-R*T*X**2-(P*BK**2+BK*R*T-AK/(T**0.5))*X-AK*BK/(T**0.5) 
	END FUNCTION RK
	
    REAL FUNCTION DRK(P,V,T,X) !R-K导函数
		IMPLICIT NONE
		REAL::P,V,T,X
		DRK=3*P*X**2-2*R*T*X-P*BK**2-BK*R*T+AK/(T**0.5)
	END FUNCTION DRK	
END MODULE NEWTON
	
	
PROGRAM MAIN
	USE IFQWIN !包含QuickWin和常用库
	implicit none
	integer(kind=4)::results !results保存调用函数之后的返回值
	logical(kind=4)::res	!res保存调用函数之后返回的LOGICAL型变量
	type(qwinfo)::winfo	!qwinfo型变量winfo用以储存窗口信息
	type(windowconfig)::wc	!windowconfig型变量用以储存窗口设置信息
	winfo.type = QWIN$MAX	!最大化窗口	
	res = GETWINDOWCONFIG(wc)
	results = SETWSIZEQQ (0, winfo)
	results = SETEXITQQ (QWIN$EXITPERSIST)
	!维持程序运行
	do while (.true.)
	end do
END PROGRAM MAIN
	
	
! 初始化界面尺寸和菜单
LOGICAL(4) FUNCTION INITIALSETTINGS( )
	USE IFQWIN
	implicit none
	integer( kind=4 ) :: results
	logical(kind=4)::res
	type (qwinfo) :: winfo
	external:: Dialoginput,InfoWindow
	winfo.w = 520	!设置窗口宽度
	winfo.h = 420	!设置窗口高度
	winfo.x=650	!设置窗口左上角初始坐标
	winfo.y=250
	winfo.type = QWIN$SET
	results = SetWSizeQQ(QWIN$FRAMEWINDOW, winfo)	!设置窗口大小
	res = appendmenuqq(1, $MENUENABLED, ' Start 'C,Dialoginput)	!设置菜单
	res = appendmenuqq(2, $MENUENABLED, ' Info...'C,InfoWindow)	!设置Info窗口

	INITIALSETTINGS= .true.
END FUNCTION INITIALSETTINGS
	
	
! 点击“开始”触发的回调子程序
subroutine Dialoginput(checked)
	USE IFWIN	!包含Win32 API例程
	USE IFLOGM	!包含对话框例程
	USE CONTROLS	!包含控件ID信息
	implicit none
	integer(kind=4)::results
	logical(kind=4)::retlog,checked
	type(dialog)::datadlg	!一级窗口为datadlg
	external:: dialogbasedApply	!声明外部例程

	! 调用dlgInit函数以初始化对话框
	retlog = DLGINIT(IDD_DIALOG1,datadlg)
	! 设置按钮“求解”的回调函数
	retlog = DlgSetSub(datadlg, IDOK, dialogbasedApply)	!IDOK为默认的确定按钮
														!IDCANCEL为默认的取消按钮
	! 激活对话框
	results = DLGMODAL (datadlg)
	! 释放占用资源
	call dlgUnInit(datadlg)
END subroutine Dialoginput
	
subroutine InfoWindow(checked)
	USE IFWIN	!包含Win32 API例程
	USE IFLOGM	!包含对话框例程
	USE CONTROLS	!包含控件ID信息
	implicit none
	integer(kind=4)::results
	logical(kind=4)::retlog,checked
	type(dialog)::dial	!一级窗口为datadlg

	! 调用dlgInit函数以初始化对话框
	retlog = DLGINIT(IDD_DIALOG2,dial)
	! 激活对话框
	results = DLGMODAL (dial)
	! 释放占用资源
	call dlgUnInit(dial)
END subroutine InfoWindow
	
	
! 按钮“求解”触发的回调子程序
subroutine dialogbasedApply( dlg, id, callbacktype )
	!DEC$ ATTRIBUTES DEFAULT :: dialogbasedApply
	USE IFLOGM
	USE CONTROLS
	USE NEWTON
	
	implicit none
	type(dialog) dlg	!二级子窗口为dlg
	logical*4 results
	integer(kind=4)::id, callbacktype,i
	real m1,m2,m3,prs,tmp,vol
	character(len=30)::str,str1,str2,str3
	
	! 提取编辑框中的数据
	results = DlgGet(dlg,IDC_EDIT1,str1)
	read(UNIT=str1,FMT=*,iostat=i)tmp
	results = DlgGet(dlg,IDC_EDIT2,str2)
	read(UNIT=str2,FMT=*,iostat=i)prs
	results = DlgGet(dlg,IDC_EDIT3,str3)
	read(UNIT=str3,FMT=*,iostat=i)vol
	prs=prs*1e6	!MPa->Pa
	
	!调用内核算法函数求解质量
	m2=vansolve(prs,vol,tmp)
	m3=rksolve(prs,vol,tmp)
	m1=ML
	
	!用户非法输入
	if(m1<=0.OR.m2<=0.OR.m3<=0)then
		m1=0
		m2=0
		m3=0
	end if
	
	!结果写入静态文本框	
	write(str,*)m1
	results = DlgSet(dlg,IDC_STATIC1,str)
	write(str,*)m2
	results = DlgSet(dlg,IDC_STATIC4,str)
	write(str,*)m3
	results = DlgSet(dlg,IDC_STATIC2,str)
	
END subroutine dialogbasedApply
	
	

2.8 生成exe

程序界面如下:

在这里插入图片描述


参考资料

[1] 周振红,徐进军等. Intel Visual Fortran 应用程序开发. 黄河水利出版社,2006.

[2] 汪华. Intel Visual Fortran 窗口程序设计实例. http://blog.renren.com/blog/254997247/860686026

写在最后

本来是专业基础课的课程作业,只需要编程计算出结果就可以了,当时灵光一闪想到模仿Refprop写一个带有UI的应用程序,于是就做下来了,感觉还挺有趣的。

吐槽一下,原来宇宙最强IDE–VS也有不少bug,还有CSDN什么时候才能支持Fortran语言的代码块😂

另外,Fortran并不擅长于应用程序设计,科学计算才是正道 [狗头] [狗头]

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值