This commit is contained in:
tp 2025-05-09 17:45:43 +08:00
commit 027e45867b
20 changed files with 2259 additions and 0 deletions

View File

@ -0,0 +1,74 @@
using Microsoft.AspNetCore.Mvc;
using FortranWebApi.Models;
using FortranWebApi.Services;
using System.Text.Json;
namespace FortranWebApi.Controllers
{
[ApiController]
[Route("[controller]")]
public class FortranCalculateController : ControllerBase
{
private readonly FortranInteropService _fortranService;
private readonly ILogger<FortranCalculateController> _logger;
public FortranCalculateController(FortranInteropService fortranService, ILogger<FortranCalculateController> logger)
{
_fortranService = fortranService;
_logger = logger;
}
[HttpPost]
public IActionResult Post([FromBody] FortranRequestWrapper wrapper)
{
try
{
if (string.IsNullOrEmpty(wrapper.Text))
{
return BadRequest(new
{
message = "请求文本不能为空",
success = false,
data = (object)null
});
}
string result = _fortranService.ProcessFortranRequest(wrapper.Text);
// 使用驼峰命名法解析结果
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
WriteIndented = true
};
var resultObj = JsonSerializer.Deserialize<FortranRequest>(result, options);
// 返回新的格式
var response = new ApiResponse<FortranRequest>
{
Message = "Success",
Success = true,
Data = new ApiResponseData<FortranRequest>
{
Value = resultObj
}
};
return Ok(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "处理Fortran请求时发生错误");
return StatusCode(500, new
{
message = ex.Message,
success = false,
data = (object)null
});
}
}
}
}

88
Dockerfile Normal file
View File

@ -0,0 +1,88 @@
# ===== 第一阶段:构建阶段 =====
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
# 配置 NuGet 使用国内镜像源
RUN dotnet nuget add source https://mirrors.cloud.tencent.com/nuget/ \
&& dotnet nuget disable source nuget.org
# 配置 apt-get 使用 apt-cacher-ng 作为代理
RUN echo 'Acquire::http::Proxy "http://192.168.1.140:3142";' > /etc/apt/apt.conf.d/01proxy
# 创建并配置 Debian 镜像源
RUN echo "deb https://mirrors.ustc.edu.cn/debian/ bookworm main contrib non-free non-free-firmware" > /etc/apt/sources.list && \
echo "deb https://mirrors.ustc.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
echo "deb https://mirrors.ustc.edu.cn/debian-security bookworm-security main contrib non-free non-free-firmware" >> /etc/apt/sources.list
# 允许使用不安全的软件源
RUN echo 'Acquire::AllowInsecureRepositories "true";' > /etc/apt/apt.conf.d/99allow-insecure && \
echo 'Acquire::AllowDowngradeToInsecureRepositories "true";' >> /etc/apt/apt.conf.d/99allow-insecure
WORKDIR /src
# 安装 Fortran 编译器
RUN apt-get update && apt-get install -y --no-install-recommends \
gfortran \
&& rm -rf /var/lib/apt/lists/*
# 复制 Fortran 源文件并编译
COPY Fortran/*.f90 /src/Fortran/
WORKDIR /src/Fortran
# 编译所有 .f90 文件为 .o 文件
RUN for file in *.f90; do gfortran -fPIC -c "$file" -o "${file%.f90}.o"; done
# 链接所有 .o 文件为共享库
RUN gfortran -shared *.o -o libSUB_SHUILIANGJISUAN.so
# 回到主工作目录
WORKDIR /src
# 只复制项目文件
COPY ["FortranWebApi.csproj", "./"]
RUN dotnet restore
# 复制源代码
COPY . .
# 合并 build 和 publish 命令
RUN dotnet publish -c Release -o /app/publish
# 复制编译好的 .so 文件到发布目录
RUN mkdir -p /app/publish && \
cp /src/Fortran/libSUB_SHUILIANGJISUAN.so /app/publish/
# ===== 第二阶段:运行阶段 =====
FROM mcr.microsoft.com/dotnet/aspnet:8.0
# 配置 apt-get 使用 apt-cacher-ng 作为代理
RUN echo 'Acquire::http::Proxy "http://192.168.1.140:3142";' > /etc/apt/apt.conf.d/01proxy
# 创建并配置 Debian 镜像源
RUN echo "deb https://mirrors.ustc.edu.cn/debian/ bookworm main contrib non-free non-free-firmware" > /etc/apt/sources.list && \
echo "deb https://mirrors.ustc.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
echo "deb https://mirrors.ustc.edu.cn/debian-security bookworm-security main contrib non-free non-free-firmware" >> /etc/apt/sources.list
# 允许使用不安全的软件源
RUN echo 'Acquire::AllowInsecureRepositories "true";' > /etc/apt/apt.conf.d/99allow-insecure && \
echo 'Acquire::AllowDowngradeToInsecureRepositories "true";' >> /etc/apt/apt.conf.d/99allow-insecure
# 安装运行时依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
libc6-dev \
libgfortran5 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=build /app/publish .
# 确保 .so 文件的权限正确
RUN chmod 755 /app/libSUB_SHUILIANGJISUAN.so
# 设置 LD_LIBRARY_PATH
ENV LD_LIBRARY_PATH=/app
# 设置端口和监听地址
ENV ASPNETCORE_URLS=http://+:5000
EXPOSE 5000
ENTRYPOINT ["dotnet", "FortranWebApi.dll"]

120
Fortran/1.json Normal file
View File

@ -0,0 +1,120 @@
{
"fortranSourceFile": "D:\\\u5DE5\u4F5C2025\\\u7B97\u6CD5\u5E73\u53F0\\dll\\\u5355\u6CB3\u9053\u4E00\u7EF4\u6C34\u52A8\u529B\u6A21\u578B\\SUB_SHUILIANGJISUAN - \u526F\u672C.f90",
"fortranFunctionName": "SUB_SHUILIANGJISUAN",
"projectName": "FortranWebApi",
"outputDirectory": "D:\\\u5DE5\u4F5C2025\\\u7B97\u6CD5\u5E73\u53F0\\\u8F93\u51FA\u5E93\\\u5355\u6CB3\u9053\u4E00\u7EF4\u6C34\u52A8\u529B\u6A21\u578B",
"parameters": [
{
"name": "NDATA",
"dataType": "Integer",
"arrayType": "Scalar",
"direction": "Input",
"description": "",
"isSelected": true
},
{
"name": "LONG",
"dataType": "Float",
"arrayType": "Scalar",
"direction": "Input",
"description": "",
"isSelected": true
},
{
"name": "INbd",
"dataType": "Integer",
"arrayType": "Scalar",
"direction": "Input",
"description": "",
"isSelected": true
},
{
"name": "INzd",
"dataType": "Integer",
"arrayType": "Scalar",
"direction": "Input",
"description": "",
"isSelected": true
},
{
"name": "INsm",
"dataType": "Integer",
"arrayType": "Scalar",
"direction": "Input",
"description": "",
"isSelected": true
},
{
"name": "INrough",
"dataType": "Integer",
"arrayType": "Scalar",
"direction": "Input",
"description": "",
"isSelected": true
},
{
"name": "ZZ0",
"dataType": "Float",
"arrayType": "Scalar",
"direction": "Input",
"description": "",
"isSelected": true
},
{
"name": "DB",
"dataType": "Integer",
"arrayType": "Scalar",
"direction": "Input",
"description": "",
"isSelected": true
},
{
"name": "UQ",
"dataType": "Float",
"arrayType": "OneDimensional",
"direction": "Input",
"description": "",
"isSelected": true
},
{
"name": "DQH",
"dataType": "Float",
"arrayType": "OneDimensional",
"direction": "Input",
"description": "",
"isSelected": true
},
{
"name": "dt",
"dataType": "Float",
"arrayType": "Scalar",
"direction": "Input",
"description": "",
"isSelected": true
},
{
"name": "OUTT",
"dataType": "Float",
"arrayType": "OneDimensional",
"direction": "Output",
"description": "",
"isSelected": true
},
{
"name": "OUTQ",
"dataType": "Float",
"arrayType": "OneDimensional",
"direction": "Output",
"description": "",
"isSelected": true
},
{
"name": "OUTH",
"dataType": "Float",
"arrayType": "OneDimensional",
"direction": "Output",
"description": "",
"isSelected": true
}
]
}

BIN
Fortran/1.xlsx Normal file

Binary file not shown.

161
Fortran/SUB_BOUND.f90 Normal file
View File

@ -0,0 +1,161 @@
! ==================================================================================
!
! ==================================================================================
!
! ndata个数据点1
!
! Q=C1*B*e*dZ^C2Z>Ztide且Z>Zctr时开闸
! Zctr是挡潮闸运行控制水位
! ----------------------------------------------------------------------------------
SUBROUTINE SUB_BOUND( NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river ,&
tstep ,&
Ns ,&
Pc ,&
Nrc ,&
Lc ,&
Dric ,&
Qj ,&
Asave ,&
UB1 ,&
UB2 ,&
DB1 ,&
DB2 ,&
NUB ,&
NDB ,&
UBV ,&
Aphi ,&
DBV ,&
Gate ,&
ql ,&
Zctr ,&
dt ,&
sita ,&
Bsor1 ,&
Bsor2 ,&
Z0 ,&
Q0 ,&
Z ,&
Q ,&
V ,&
condu ,&
condd )
INTEGER::NRIVER
INTEGER::NSECT
INTEGER::MRIVER
INTEGER::KRC
INTEGER::NDATA
INTEGER RIVER,TSTEP
! node
INTEGER::Ns(nriver),Pc(nriver),Nrc(krc,nriver),Lc(krc,nriver)
REAL::Dric(krc,nriver),Qj(ndata,krc,nriver),Asave(krc,nriver)
! boundary
INTEGER::UB1(nriver),UB2(nriver),DB1(nriver),DB2(nriver),NUB(2,nriver),NDB(2,nriver)
REAL::UBV(ndata,nriver),Aphi(2,nriver),DBV(ndata,nriver),Gate(4,nriver),ql,Zctr
! calcu
REAL::dt,sita,Bsor1,Bsor2
! zzqq
REAL::Z0(nsect,nriver),Q0(nsect,nriver),Z(nsect,nriver),Q(nsect,nriver),V(nsect,nriver)
! r_bv
REAL::condu,condd(3)
REAL:: ZQ(NDATA)
TC=DT*FLOAT(TSTEP)/3600.0
!
IF(UB2(RIVER).EQ.1)THEN !
DO II=1,NDATA
ZQ(II)=UBV(II,RIVER)
END DO
CALL INT_A(TC,NDATA,ZQ,FC)
CONDU=FC
ELSEIF(UB2(RIVER).EQ.2)THEN !
IR=NUB(1,RIVER)
IS=NUB(2,RIVER)
IF(UB1(RIVER).EQ.1)THEN
CONDU=Z(IS,IR)
END IF
IF(UB1(RIVER).EQ.2)THEN
CONDU=Q(IS+1,IR)
END IF
END IF
!
IF(DB2(RIVER).EQ.1)THEN
DO II=1,NDATA
ZQ(II)=DBV(II,RIVER)
END DO
CALL INT_A(TC,NDATA,ZQ,FC)
IF(DB1(RIVER).EQ.1)THEN
CONDD(1)=1.0
CONDD(2)=0.0
CONDD(3)=FC
ELSE IF(DB1(RIVER).EQ.2)THEN
CONDD(1)=0.0
CONDD(2)=1.0
CONDD(3)=FC
ELSE IF(DB1(RIVER).EQ.3)THEN
ZU0=Z0(NS(RIVER)-1,RIVER)
ZU=Z0(NS(RIVER)-1,RIVER)
ZU=(1-BSOR1)*ZU0+BSOR1*ZU
B=GATE(1,RIVER)
QMAX=GATE(2,RIVER)
C1=GATE(3,RIVER)
C2=GATE(4,RIVER)
IF(ZU>FC.AND.ZU>=ZCTR)THEN
DZ=ZU-FC
EK=5.0*DZ
IF(DZ<0.15)THEN
E=0.0
END IF
QQ=C1*B*EK*DZ**C2
QQ=(1-BSOR1)*Q0(NS(RIVER),RIVER)+BSOR1*QQ
IF(QQ>QMAX)THEN
QQ=QMAX
END IF
CONDD(1)=0.0
CONDD(2)=1.0
CONDD(3)=QQ
ELSE
CONDD(1)=0.0
CONDD(2)=1.0
CONDD(3)=0.0
END IF
END IF
ELSEIF(DB2(RIVER).EQ.2)THEN
IR=NDB(1,RIVER)
IS=NDB(2,RIVER)
IF(DB1(RIVER).EQ.1)THEN
CONDD(1)=1.0
CONDD(2)=0.0
CONDD(3)=Z(IS,IR)
ELSE
AH=APHI(1,RIVER)
PHI=APHI(2,RIVER)
IF(Z(NS(RIVER),RIVER)>=Z(IS,IR))THEN
QQ=AH*PHI*SQRT(2*9.8*(Z(NS(RIVER),RIVER)-Z(IS,IR)))
ELSE
QQ=-AH*PHI*SQRT(2*9.8*(Z(IS,IR)-Z(NS(RIVER),RIVER)))
END IF
QQ=(1-BSOR2)*Q0(NS(RIVER),RIVER)+BSOR2*QQ
CONDD(1)=0.0
CONDD(2)=1.0
CONDD(3)=QQ
END IF
END IF
END SUBROUTINE SUB_BOUND

29
Fortran/SUB_GETNXT.F90 Normal file
View File

@ -0,0 +1,29 @@
SUBROUTINE SUB_GETNXT(IIN)
IMPLICIT NONE
INTEGER::IIN
INTEGER::I
CHARACTER*80 DATALN
CHARACTER*1 CH
1 READ(IIN,'(A80)') DATALN
DO I = 1, 80
CH = DATALN(I:I)
IF ( CH .EQ. '|') GOTO 1 ! |
IF ( CH .EQ. '*') GOTO 1 ! *
IF ( CH .EQ. ' ') GOTO 10 !TAB空格
IF ( CH .NE. ' ') GOTO 40 !
10 END DO
GOTO 1
40 BACKSPACE IIN
RETURN
END SUBROUTINE SUB_GETNXT

42
Fortran/SUB_INT_A.f90 Normal file
View File

@ -0,0 +1,42 @@
!
SUBROUTINE INT_A( TC ,& ! //
N ,& !
F ,& ! 线
FC ) ! //
IMPLICIT NONE
!
REAL::TC
INTEGER::N
REAL::F(N)
!
REAL::FC
!
INTEGER::I
REAL::T
REAL::T1
REAL::T2
!
DO I=1,N
T=1.0*FLOAT(I-1)
IF(TC.EQ.T)THEN
FC=F(I)
END IF
END DO
! 线
DO I=1,N-1
T1=1.0*FLOAT(I-1)
T2=1.0*FLOAT(I)
IF(TC.GT.T1.AND.TC.LT.T2)THEN
FC=F(I)+(TC-T1)*(F(I+1)-F(I))/(T2-T1)
EXIT
END IF
END DO
END SUBROUTINE INT_A

339
Fortran/SUB_QZ.f90 Normal file
View File

@ -0,0 +1,339 @@
! ==================================================================================
! Z和流量Q的子程序
! ==================================================================================
subroutine sub_QZ( NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river ,&
tstep ,&
Ns ,&
Pc ,&
Nrc ,&
Lc ,&
Dric ,&
Qj ,&
Asave ,&
ds ,&
bd ,&
zd ,&
sm ,&
rough ,&
UB1 ,&
UB2 ,&
DB1 ,&
DB2 ,&
NUB ,&
NDB ,&
UBV ,&
Aphi ,&
DBV ,&
Gate ,&
ql ,&
Zctr ,&
dt ,&
sita ,&
Bsor1 ,&
Bsor2 ,&
Z0 ,&
Q0 ,&
Z ,&
Q ,&
V ,&
condu ,&
condd ,&
Bs ,&
As ,&
Rs ,&
Cs )
INTEGER::NRIVER
INTEGER::NSECT
INTEGER::MRIVER
INTEGER::KRC
INTEGER::NDATA
integer River,tstep
REAL Qjj(ndata)
real L(nsect),M(nsect),P(nsect),R(nsect)
! node
INTEGER::Ns(nriver),Pc(nriver),Nrc(krc,nriver),Lc(krc,nriver)
REAL::Dric(krc,nriver),Qj(ndata,krc,nriver),Asave(krc,nriver)
! section
REAL::ds(mriver,nriver),bd(nsect,nriver),zd(nsect,nriver),sm(nsect,nriver), rough(nsect,nriver)
! boundary
INTEGER::UB1(nriver),UB2(nriver),DB1(nriver),DB2(nriver),NUB(2,nriver),NDB(2,nriver)
REAL::UBV(ndata,nriver),Aphi(2,nriver),DBV(ndata,nriver),Gate(4,nriver),ql,Zctr
! calcu
REAL::dt,sita,Bsor1,Bsor2
! zzqq
REAL::Z0(nsect,nriver),Q0(nsect,nriver),Z(nsect,nriver),Q(nsect,nriver),V(nsect,nriver)
! r_bv
REAL::condu,condd(3)
! BARC
REAL::Bs,As,Rs,Cs
f(t10,t1,t20,t2)=0.5*(sita*(t2+t1)+(1.0-sita)*(t20+t10))
tc=dt*float(tstep)/3600.0
if(UB1(river).eq.1)Z(1,river)=condu
if(UB1(river).eq.2)Q(1,river)=condu
! ----------------------------------------------------------------------------------
! LMPR
! ----------------------------------------------------------------------------------
P(1)=condu
R(1)=0.0
call sub_sect( NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river,1,Z0(1,river),&
ds ,&
bd ,&
zd ,&
sm ,&
rough ,&
Bs ,&
As ,&
Rs ,&
Cs )
BB10=Bs
AA10=As
RR10=Rs
CC10=Cs
call sub_sect( NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river,1,Z(1,river),&
ds ,&
bd ,&
zd ,&
sm ,&
rough ,&
Bs ,&
As ,&
Rs ,&
Cs )
BB1=Bs
AA1=As
RR1=Rs
CC1=Cs
do 100 k=1,Ns(river)-1
Ik=0
do 20 i=1,Pc(river)
if(Nrc(i,river).eq.k)then
Ic=i
Ik=1
end if
20 continue
if(Ik.ne.1)then
! ------------------------------------------------------
!
!
! M点处的水面宽BARQ和水位Z
! ------------------------------------------------------
call sub_sect( NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river,k+1,Z0(k+1,river),&
ds ,&
bd ,&
zd ,&
sm ,&
rough ,&
Bs ,&
As ,&
Rs ,&
Cs )
BB20=Bs
AA20=As
RR20=Rs
CC20=Cs
call sub_sect( NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river,k+1,Z(k+1,river),&
ds ,&
bd ,&
zd ,&
sm ,&
rough ,&
Bs ,&
As ,&
Rs ,&
Cs )
BB2=Bs
AA2=As
RR2=Rs
CC2=Cs
Bm=f(BB10,BB1,BB20,BB2)
Am=f(AA10,AA1,AA20,AA2)
Rm=f(RR10,RR1,RR20,RR2)
Cm=f(CC10,CC1,CC20,CC2)
Qm=f(Q0(k,river),Q(k,river),Q0(k+1,river),Q(k+1,river))
Zm=f(Z0(k,river),Z(k,river),Z0(k+1,river),Z(k+1,river))
! ------------------------------------------------------
!
! ------------------------------------------------------
a1=1.0
c1=2.0*sita*dt/(ds(k,river)*Bm)
e1=Z0(k,river)+Z0(k+1,river)+(1.0-sita)/sita*c1*(Q0(k,river)-Q0(k+1,river))+2.0*dt*ql/Bm
a2=2.0*sita*dt/ds(k,river)*((Qm/Am)**2.0*Bm-9.8*Am)
c2=1.0-4.0*sita*dt/ds(k,river)*Qm/Am
d2=1.0+4.0*sita*dt/ds(k,river)*Qm/Am
call sub_sect( NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river,k,Zm,&
ds ,&
bd ,&
zd ,&
sm ,&
rough ,&
Bs ,&
As ,&
Rs ,&
Cs )
AZm1=As
call sub_sect( NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river,k+1,Zm,&
ds ,&
bd ,&
zd ,&
sm ,&
rough ,&
Bs ,&
As ,&
Rs ,&
Cs )
AZm2=As
e2=(1.0-sita)/sita*a2*(Z0(k+1,river)-Z0(k,river))+(1.0-4.0*(1.0-sita)*dt/ds(k,river)*Qm/Am)*Q0(k+1,river)+(1.0+4.0*(1.0-sita)*dt/ds(k,river)*Qm/Am)*Q0(k,river)+2.0*dt*(Qm/Am)**2.0*(AZm2-AZm1)/ds(k,river)-2.0*dt*9.8*abs(Qm)*Qm/(Am*Cm*Cm*Rm)
else
! --------------------------------------------------------
! Lc=1Lc=2Lc=3
! --------------------------------------------------------
if(Lc(Ic,river).eq.1)then
do 35 j=1,nriver
if(NUB(1,j).eq.river.and.NUB(2,j).eq.Nrc(Ic,river))then
a1=0.0
c1=1.0
e1=Dric(Ic,river)*Q(1,j)
a2=1.0
c2=0.0
d2=0.0
e2=0.0
elseif(NDB(1,j).eq.river.and.NDB(2,j).eq.Nrc(Ic,river))then
a1=0.0
c1=1.0
e1=Dric(Ic,river)*Q(Ns(j),j)
a2=1.0
c2=0.0
d2=0.0
e2=0.0
end if
35 continue
elseif(Lc(Ic,river).eq.2)then
do 55 ii=1,ndata
Qjj(ii)=Qj(ii,Ic,river)
55 continue
call int_a(tc,ndata,Qjj,fc)
a1=0.0
c1=1.0
e1=Dric(Ic,river)*fc
a2=1.0
c2=0.0
d2=0.0
e2=0.0
elseif(Lc(Ic,river).eq.3)then
a1=0.5*Asave(Ic,river)/dt
c1=1.0
e1=Asave(Ic,river)*Z0(k,river)/dt
a2=1.0
c2=0.0
d2=0.0
e2=0.0
end if
end if
! ------------------------------------------------------
! 线
! ------------------------------------------------------
if(UB1(river).eq.1)then
Y1=a1*R(k)-c1
Y2=a2*R(k)+c2
Y3=e1-a1*P(k)
Y4=e2-a2*P(k)
Y5=a1*Y2+a2*Y1
L(k+1)=(a2*Y3+a1*Y4)/Y5
M(k+1)=-(a1*d2+a2*c1)/Y5
P(k+1)=(Y2*Y3-Y1*Y4)/Y5
R(k+1)=(d2*Y1-c1*Y2)/Y5
end if
! ------------------------------------------------------
! 线
! ------------------------------------------------------
if(UB1(river).eq.2)then
Y1=a1-c1*R(k)
Y2=a2+c2*R(k)
Y3=e1+c1*P(k)
Y4=e2-c2*P(k)
Y5=d2*Y1-c1*Y2
L(k+1)=(d2*Y3-c1*Y4)/Y5
M(k+1)=-(a1*d2+a2*c1)/Y5
P(k+1)=(Y1*Y4-Y2*Y3)/Y5
R(k+1)=(a1*Y2+a2*Y1)/Y5
end if
BB10=BB20
AA10=AA20
RR10=RR20
CC10=CC20
BB1=BB2
AA1=AA2
RR1=RR2
CC1=CC2
100 continue
! ----------------------------------------------------------------------------------
! Q和水位Z
! ----------------------------------------------------------------------------------
if(UB1(river).eq.1)then
Q(Ns(river),river)=(condd(3)-condd(1)*P(Ns(river)))/(condd(1)*R(Ns(river))+condd(2))
do 120 i=Ns(river),2,-1
Z(i,river)=P(i)+R(i)*Q(i,river)
Q(i-1,river)=L(i)+M(i)*Q(i,river)
120 continue
end if
if(UB1(river).eq.2)then
Z(Ns(river),river)=(condd(3)-condd(2)*P(Ns(river)))/(condd(2)*R(Ns(river))+condd(1))
do 130 i=Ns(river),2,-1
Q(i,river)=P(i)+R(i)*Z(i,river)
Z(i-1,river)=L(i)+M(i)*Z(i,river)
130 continue
end if
return
end

45
Fortran/SUB_SECT.f90 Normal file
View File

@ -0,0 +1,45 @@
! ==================================================================================
!
! ==================================================================================
subroutine sub_sect( NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river ,&
Is ,&
Zs ,&
ds ,&
bd ,&
zd ,&
sm ,&
rough ,&
Bs ,&
As ,&
Rs ,&
Cs )
INTEGER::NRIVER
INTEGER::NSECT
INTEGER::MRIVER
INTEGER::KRC
INTEGER::NDATA
integer River
! section
REAL::ds(mriver,nriver),bd(nsect,nriver),zd(nsect,nriver),sm(nsect,nriver), rough(nsect,nriver)
! BARC
REAL::Bs,As,Rs,Cs
! ----------------------------------------------------------------------------------
! BsAsRsCsZs的断面要素
! ----------------------------------------------------------------------------------
h=Zs-zd(Is,river)
Bs=bd(Is,river)+2.0*sm(Is,river)*h
As=(bd(Is,river)+sm(Is,river)*h)*h
Sl=bd(Is,river)+2*h*sqrt(1+sm(Is,river)**2.0)
Rs=As/Sl
Cs=Rs**(1.0/6.0)/rough(Is,river)
return
end

View File

@ -0,0 +1,563 @@
! SUB_SHUILIANGJISUAN.f90
!
! FUNCTIONS/SUBROUTINES exported from SUB_SHUILIANGJISUAN.dll:
! SUB_SHUILIANGJISUAN - subroutine
!
subroutine SUB_SHUILIANGJISUAN( NDATA ,& ! //
LONG ,& !
INbd ,& !
INzd ,& !
INsm ,& !
INrough ,& !
ZZ0 ,& !
DB ,& ! 1. 2.
UQ ,& !
DQH ,& ! /
dt ,& !
OUTT ,& !
OUTQ ,& !
OUTH ) !
! Expose subroutine SUB_SHUILIANGJISUAN to users of this DLL
!
!DEC$ ATTRIBUTES DLLEXPORT::SUB_SHUILIANGJISUAN
! Variables
INTEGER::FHD
INTEGER::NRIVER
INTEGER::NSECT
INTEGER::MRIVER
INTEGER::KRC
INTEGER::NDATA
REAL::ZZ0
INTEGER::DB
REAL::UQ(NDATA)
REAL::DQH(NDATA)
REAL::OUTQ(1000)
REAL::OUTH(1000)
REAL::OUTT(1000)
REAL::DDGC !
REAL::MAXH
INTEGER::MY ! 1. 2.
integer River,tstep
! node
INTEGER,ALLOCATABLE::Ns(:),Pc(:),Nrc(:,:),Lc(:,:)
REAL,ALLOCATABLE::Dric(:,:),Qj(:,:,:),Asave(:,:)
! section
REAL,ALLOCATABLE::ds(:,:),bd(:,:),zd(:,:),sm(:,:), rough(:,:)
! boundary
INTEGER,ALLOCATABLE::UB1(:),UB2(:),DB1(:),DB2(:),NUB(:,:),NDB(:,:)
REAL,ALLOCATABLE::UBV(:,:),Aphi(:,:),DBV(:,:),Gate(:,:)
REAL::ql,Zctr
! calcu
REAL::dt,sita,Bsor1,Bsor2
! zzqq
REAL,ALLOCATABLE::Z0(:,:),Q0(:,:),Z(:,:),Q(:,:),V(:,:)
! r_bv
REAL::condu,condd(3)
! BARC
REAL::Bs,As,Rs,Cs
REAL,ALLOCATABLE::Qp(:)
REAL,ALLOCATABLE::Zc(:,:),Qc(:,:)
INTEGER::I
INTEGER::II
REAL::LONG
REAL::INbd
REAL::INzd
REAL::INsm
REAL::INrough
INTEGER::TEMP
!OPEN(1,FILE='OUTFHDL.TXT')
!WRITE(1,*)' ! 计算时刻数 ', NDATA ,&
! ' ! 河道长度 ', LONG ,&
! '! 断面底宽 ', INbd ,&
! '! 底高程 ' , INzd ,&
! '! 边坡系数 ', INsm ,&
! ' ! 糙率 ' , INrough ,&
! ' ! 计算区域初始水位 ', ZZ0 ,&
! ' ! 下游边界条件类型 1.水位边界 2.流量边界 ', DB ,&
! ' ! 上游流量过程 ' , UQ ,&
! ' ! 上游水位/流量过程 ', DQH ,&
! ' ! 计算步长( 秒) ', dt ,&
! '! 计算断面堤顶高程 ', DDGC
FHD = 0
TEMP = 0
! 1.
!open(1,file='河道组态数据.TXT')
! 1. 2. 3. 4.
!CALL SUB_GETNXT(1)
!read(1,*)
nriver = 1
nsect = 6
krc = 0
mriver=nsect - 1 ! -1
ALLOCATE(Ns(nriver),Pc(nriver),Nrc(krc,nriver),Lc(krc,nriver))
ALLOCATE(Dric(krc,nriver),Qj(ndata,krc,nriver),Asave(krc,nriver))
ALLOCATE(ds(mriver,nriver),bd(nsect,nriver),zd(nsect,nriver),sm(nsect,nriver), rough(nsect,nriver))
ALLOCATE(UB1(nriver),UB2(nriver),DB1(nriver),DB2(nriver),NUB(2,nriver),NDB(2,nriver))
ALLOCATE(UBV(ndata,nriver),Aphi(2,nriver),DBV(ndata,nriver),Gate(4,nriver))
ALLOCATE(Z0(nsect,nriver),Q0(nsect,nriver),Z(nsect,nriver),Q(nsect,nriver),V(nsect,nriver))
ALLOCATE(Qp(ndata))
ALLOCATE(Zc(nsect,nriver),Qc(nsect,nriver))
! 1. 2. 3.
!
! CALL SUB_GETNXT(1)
! DO I = 1,nriver
!read(1,*)II,Ns(i),Pc(i)
! END DO
Ns(1) = 6
Pc(1) = 0
!! 1. 2. 3. 4.
! 123
! 1-1
Nrc = 0
Lc = 0
Dric = 0
! DO J = 1,nriver
! DO I = 1,Pc(J)
! CALL SUB_GETNXT(1)
!read(1,*)II,Nrc(i,j),Lc(i,j),Dric(i,j)
! END DO
! END DO
!
Qj = 0.0
!DO k=1,nriver
! DO j=1,Pc(k)
! CALL SUB_GETNXT(1)
! if(Lc(j,k).eq.2)then
! read(1,*)II,II,(Qj(i,j,k),i=1,ndata)
! end if
! END DO
! END DO
! m2
Asave = 0.0
!DO k=1,nriver
! DO j=1,Pc(k)
! if(Lc(j,k).eq.3)then
! CALL SUB_GETNXT(1)
! read(1,*)Asave(j,k)
! end if
! END DO
! END DO
! 2.
! CALL SUB_GETNXT(1)
! ds = 0.0
! DO J = 1,nriver
! DO I = 1,Ns(j)-1
!read(1,*)II,II,ds(i,j) ! 0
! END DO
! END DO
ds(:,1) = LONG/5
! CALL SUB_GETNXT(1)
! bd = 0.0
! zd = 0.0
! sm = 0.0
! rough = 0.0
! DO j=1,nriver
! DO I =1,Ns(j)
!read(1,*)II,II,bd(i,j),zd(i,j),sm(i,j),rough(i,j) !
! END DO
! END DO
bd = INbd
zd = INzd
sm = INsm
rough = INrough
! 3.
! CALL SUB_GETNXT(1)
! read(1,*)ZZ0,Zctr ! ZZ0,Zctr,
Zctr = 0.0
Scanal = 0.0 ! Scanalql
DO J = 1,nriver
DO I = 1,Ns(j)-1
Scanal = Scanal + ds(i,j) ! 0
END DO
END DO
!CALL SUB_GETNXT(1)
!DO I = 1,ndata
! read(1,*)II,Qp(i) ! Qp
! END DO
Qp = 0.0
! UB1上游边界条件类型112,3
! UB2上游边界条件类型212
! DB1下游边界条件类型112,3
! DB2下游边界条件类型212
!CALL SUB_GETNXT(1)
! DO I = 1,nriver
! read(1,*)II,UB1(i),UB2(i),DB1(i),DB2(i)
!END DO
UB1(1) = 2
UB2(1) = 1
DB1(1) = DB
DB2(1) = 1
!CALL SUB_GETNXT(1)
UBV = 0.0
NUB = 0
!DO i=1,nriver
! if(UB2(i).eq.1)then !
! read(1,*)II,II,II,(UBV(j,i),j=1,ndata) !
! elseif(UB2(i).eq.2)then !
! read(1,*)II,II,II,(NUB(j,i),j=1,2) !
! endif
! END DO
DO I = 1,ndata
UBV(I,1) = UQ(I)
END DO
!CALL SUB_GETNXT(1)
!DO i=1,nriver
! if(DB2(i).eq.1)then !
! if(DB1(i).eq.3)then !
! read(1,*)II,II,II,(DBV(j,i),j=1,ndata),(Gate(j,i),j=1,4) ! Gate(1,i) = Gate(2,i) = Gate(3,i) = C1Gate(4,i) = C2 Q=C1*B*e*dZ^C2中的C1和C2
! ELSE
! read(1,*)II,II,II,(DBV(j,i),j=1,ndata) !
! end if
! elseif(DB2(i).eq.2)then !
! if(DB1(i).eq.2)then
! read(1,*)II,II,II,(NDB(j,i),j=1,2),(Aphi(j,i),j=1,2) !
! ELSE
! read(1,*)II,II,II,(NDB(j,i),j=1,2) !
! end if
! endif
!END DO
DO I = 1,ndata
DBV(I,1) = DQH(I)
END DO
! 4.
!CALL SUB_GETNXT(1)
!read(1,*)period,dt,sita ! perioddt,sita
!CALL SUB_GETNXT(1)
!read(1,*)sorz,sorq,epsz,epsq ! sorzsorq,epszepsq
!CALL SUB_GETNXT(1)
!read(1,*)Bsor1,Bsor2 ! Bsor1Bsor2
!close(1)
period =REAL( NDATA - 1 )
! read(1,*)dt
sita = 0.7
sorz = 0.3
sorq = 0.3
epsz = 0.01
epsq = 0.1
Bsor1 = 0.3
Bsor2 = 0.1
!
maxtstep=period*3600/dt
maxiter=1000 ! 仿
!
DO river=1,nriver
DO Is=1,Ns(river)
Z0(Is,river)=ZZ0
Q0(Is,river)=0.0
END DO
END DO
!
!open(1,file='河道1各断面水位变化.TXT')
!open(2,file='河道2各断面水位变化.TXT')
!open(3,file='河道3各断面水位变化.TXT')
!open(4,file='河道4各断面水位变化.TXT')
!
!open(11,file='河道1各断面流量变化.TXT')
!open(12,file='河道2各断面流量变化.TXT')
!open(13,file='河道3各断面流量变化.TXT')
!open(14,file='河道4各断面流量变化.TXT')
! ----------------------------------------------------------------------------------
!
! ----------------------------------------------------------------------------------
DO tstep=1,maxtstep !
TEMP = TEMP + 1
ttime=dt*tstep/3600.0 !
! write(*,15)ttime
iter=0
DO river=1,nriver
DO Is=1,Ns(river)
Z(Is,river)=Z0(Is,river)
Q(Is,river)=Q0(Is,river)
END DO
END DO
!
call int_a( ttime ,& ! //
ndata ,& !
Qp ,& ! 线
fc ) ! //
ql=fc/Scanal
25 iter=iter+1
DO river=1,nriver
DO Is=1,Ns(river)
Zc(Is,river)=Z(Is,river)
Qc(Is,river)=Q(Is,river)
END DO
END DO
DO river=1,nriver
!
call sub_bound( NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river ,&
tstep ,&
Ns ,&
Pc ,&
Nrc ,&
Lc ,&
Dric ,&
Qj ,&
Asave ,&
UB1 ,&
UB2 ,&
DB1 ,&
DB2 ,&
NUB ,&
NDB ,&
UBV ,&
Aphi ,&
DBV ,&
Gate ,&
ql ,&
Zctr ,&
dt ,&
sita ,&
Bsor1 ,&
Bsor2 ,&
Z0 ,&
Q0 ,&
Z ,&
Q ,&
V ,&
condu ,&
condd )
! Z和流量Q
call sub_QZ( NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river ,&
tstep ,&
Ns ,&
Pc ,&
Nrc ,&
Lc ,&
Dric ,&
Qj ,&
Asave ,&
ds ,&
bd ,&
zd ,&
sm ,&
rough ,&
UB1 ,&
UB2 ,&
DB1 ,&
DB2 ,&
NUB ,&
NDB ,&
UBV ,&
Aphi ,&
DBV ,&
Gate ,&
ql ,&
Zctr ,&
dt ,&
sita ,&
Bsor1 ,&
Bsor2 ,&
Z0 ,&
Q0 ,&
Z ,&
Q ,&
V ,&
condu ,&
condd ,&
Bs ,&
As ,&
Rs ,&
Cs )
END DO
dzmax=0.0
dqmax=0.0
DO river=1,nriver
DO Is=1,Ns(river)
dz=abs(Z(Is,river)-Zc(Is,river))
dq=abs(Q(Is,river)-Qc(Is,river))
if(dz.gt.dzmax)then
dzmax=dz
maxz_r=river
maxz_s=Is
end if
if(dq.gt.dqmax)then
dqmax=dq
maxq_r=river
maxq_s=Is
end if
END DO
END DO
if(dzmax.gt.epsz.or.dqmax.gt.epsq)then
! write(*,50)iter,maxz_r,maxz_s,dzmax,maxq_r,maxq_s,dqmax
DO river=1,nriver
DO Is=1,Ns(river)
Z(Is,river)=(1.0-sorz)*Zc(Is,river)+sorz*Z(Is,river)
Q(Is,river)=(1.0-sorq)*Qc(Is,river)+sorq*Q(Is,river)
END DO
END DO
if(iter.le.maxiter)then
goto 25
else
! write(*,55)
goto 600
end if
else
DO river=1,nriver
DO Is=1,Ns(river)
!
call sub_sect(NRIVER ,&
NSECT ,&
MRIVER ,&
KRC ,&
NDATA ,&
river,Is,Z(Is,river),&
ds ,&
bd ,&
zd ,&
sm ,&
rough ,&
Bs ,&
As ,&
Rs ,&
Cs )
V(Is,river)=Q(Is,river)/As
END DO
END DO
!
DO jj=1,nriver
!write(jj,80)ttime,(Z(ii,jj),ii=1,Ns(jj))
END DO
DO jj=1,nriver
jj1=jj+10
!write(jj1,80)ttime,(Q(ii,jj),ii=1,Ns(jj))
END DO
IF(FHD==1)THEN
OUTQ(TEMP ) = Q(5,1)*0.9
OUTH(TEMP ) = Z(5,1)*0.95
ELSE
OUTQ(TEMP ) = Q(5,1)
OUTH(TEMP ) = Z(5,1)
END IF
OUTT(TEMP ) = ttime
DO river=1,nriver
DO Is=1,Ns(river)
Z0(Is,river)=Z(Is,river)
Q0(Is,river)=Q(Is,river)
END DO
END DO
end if
END DO
600 close(100)
close(2)
close(3)
close(4)
close(11)
close(12)
close(13)
close(14)
15 format(55x,'time=',f10.2)
50 format(55x,'iter=',i5/1x,'maxz_r=',i5,5x,'maxz_s=',i5,5x,'dzmax=',e10.4/1x,'maxq_r=',i5,5x,'maxq_s=',i5,5x,'dqmax=',e10.4)
55 format(1x,'迭代发散')
80 format(1x,40f12.4)
!MAXH = MAXVAL(OUTH)
!IF(MAXH.GT.DDGC)THEN
! MY = 1
!ELSE
! MY = 0
!END IF
!WRITE(1,*)' ! 1.漫溢 0.未漫溢 ' , MY ,&
! '! 计算断面输出时刻 ', OUTT ,&
! '! 计算断面输出流量 ', OUTQ ,&
! '! 计算断面输出水位 ', OUTH
CLOSE(1)
END SUBROUTINE SUB_SHUILIANGJISUAN

27
FortranWebApi.csproj Normal file
View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="lib{{FortranFunctionName}}.so">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,23 @@
@FortranWebApi_HostAddress = http://localhost:5000
### 测试Fortran请求
POST {{FortranWebApi_HostAddress}}/FortranCalculate
Content-Type: application/json
{
"text": "{\"FuncName\":\"calculate_main\",\"ClassName\":\"\",\"Par\":[ {"Name":"NDATA","DataType":"0","ArrayType":"0","IsOut":"2","Data":0},
{"Name":"LONG","DataType":"1","ArrayType":"0","IsOut":"2","Data":0},
{"Name":"INbd","DataType":"0","ArrayType":"0","IsOut":"2","Data":0},
{"Name":"INzd","DataType":"0","ArrayType":"0","IsOut":"2","Data":0},
{"Name":"INsm","DataType":"0","ArrayType":"0","IsOut":"2","Data":0},
{"Name":"INrough","DataType":"0","ArrayType":"0","IsOut":"2","Data":0},
{"Name":"ZZ0","DataType":"1","ArrayType":"0","IsOut":"2","Data":0},
{"Name":"DB","DataType":"0","ArrayType":"0","IsOut":"2","Data":0},
{"Name":"UQ","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]},
{"Name":"DQH","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]},
{"Name":"dt","DataType":"1","ArrayType":"0","IsOut":"2","Data":0},
{"Name":"OUTT","DataType":"1","ArrayType":"1","IsOut":"1","Data":[]},
{"Name":"OUTQ","DataType":"1","ArrayType":"1","IsOut":"1","Data":[]},
{"Name":"OUTH","DataType":"1","ArrayType":"1","IsOut":"1","Data":[]}]}"
}

31
Models/ApiResponse.cs Normal file
View File

@ -0,0 +1,31 @@
using System.Text.Json.Serialization;
namespace FortranWebApi.Models
{
public class ApiResponse<T>
{
[JsonPropertyName("message")]
public string Message { get; set; } = "Success";
[JsonPropertyName("success")]
public bool Success { get; set; } = true;
[JsonPropertyName("data")]
public ApiResponseData<T>? Data { get; set; }
}
public class ApiResponseData<T>
{
[JsonPropertyName("contentType")]
public string? ContentType { get; set; }
[JsonPropertyName("serializerSettings")]
public object? SerializerSettings { get; set; }
[JsonPropertyName("statusCode")]
public int? StatusCode { get; set; }
[JsonPropertyName("value")]
public T? Value { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using System.Text.Json.Serialization;
namespace FortranWebApi.Models
{
public class FortranParameter
{
[JsonPropertyName("name")]
public string Name { get; set; } = string.Empty;
[JsonPropertyName("dataType")]
public string DataType { get; set; } = "0";
[JsonPropertyName("arrayType")]
public string ArrayType { get; set; } = "0";
[JsonPropertyName("isOut")]
public string IsOut { get; set; } = "2";
[JsonPropertyName("data")]
public object? Data { get; set; }
}
}

13
Models/FortranRequest.cs Normal file
View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace FortranWebApi.Models
{
public class FortranRequest
{
public string FuncName { get; set; } = string.Empty;
public string ClassName { get; set; } = string.Empty;
[JsonPropertyName("par")]
public List<FortranParameter> Parameters { get; set; } = new List<FortranParameter>();
}
}

View File

@ -0,0 +1,7 @@
namespace FortranWebApi.Models
{
public class FortranRequestWrapper
{
public string Text { get; set; } = string.Empty;
}
}

80
Program.cs Normal file
View File

@ -0,0 +1,80 @@
using Serilog;
using FortranWebApi.Services;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
// 设置库搜索路径
string currentDir = Directory.GetCurrentDirectory();
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH",
$"{Environment.GetEnvironmentVariable("LD_LIBRARY_PATH")}:{currentDir}");
var builder = WebApplication.CreateBuilder(args);
// 添加健康检查服务
builder.Services.AddHealthChecks();
// 配置Serilog
Log.Logger = new LoggerConfiguration()
.WriteTo.Console(outputTemplate:
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
.WriteTo.File("logs/log-.txt",
rollingInterval: RollingInterval.Day,
outputTemplate:
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
.CreateLogger();
builder.Host.UseSerilog(); // 将Serilog添加到Host
// 配置JSON序列化选项使用驼峰命名法
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<FortranInteropService>();
// 配置CORS
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors();
app.UseAuthorization();
// 映射健康检查端点
app.MapHealthChecks("/health");
app.MapControllers();
try
{
Log.Information("启动应用程序...");
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "应用程序启动失败");
}
finally
{
Log.CloseAndFlush();
}

View File

@ -0,0 +1,14 @@
{
"profiles": {
"FortranWebApi": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://0.0.0.0:5000"
}
}
}

View File

@ -0,0 +1,565 @@
using System.Runtime.InteropServices;
using FortranWebApi.Models;
using System.Text.Json;
using System.Runtime.InteropServices.Marshalling;
using System.IO;
namespace FortranWebApi.Services
{
public class FortranInteropService
{
// 静态构造函数设置DLL导入解析器
static FortranInteropService()
{
// 添加当前目录到库搜索路径
NativeLibrary.SetDllImportResolver(typeof(FortranInteropService).Assembly, (name, assembly, path) =>
{
if (name == "libSUB_SHUILIANGJISUAN.so")
{
// 尝试从当前目录加载
string currentDir = Directory.GetCurrentDirectory();
string libraryPath = Path.Combine(currentDir, "libSUB_SHUILIANGJISUAN.so");
if (File.Exists(libraryPath))
{
return NativeLibrary.Load(libraryPath);
}
}
// 回退到默认加载行为
return IntPtr.Zero;
});
}
// DllImport声明
[DllImport("libSUB_SHUILIANGJISUAN.so", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SUB_SHUILIANGJISUAN")]
private static extern void SUB_SHUILIANGJISUAN(
ref int NDATA,
ref float LONG,
ref int INbd,
ref int INzd,
ref int INsm,
ref int INrough,
ref float ZZ0,
ref int DB,
float[] UQ,
float[] DQH,
ref float dt,
float[] OUTT,
float[] OUTQ,
float[] OUTH
);
public string ProcessFortranRequest(string requestText)
{
try
{
// 解析请求
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
WriteIndented = true
};
var request = JsonSerializer.Deserialize<FortranRequest>(requestText, options);
if (request == null)
{
throw new ArgumentException("无效的请求格式");
}
// 准备输入参数
var parameters = request.Parameters;
// 提取参数
int NDATA = GetIntParameter(parameters, "NDATA");
float LONG = GetFloatParameter(parameters, "LONG");
int INbd = GetIntParameter(parameters, "INbd");
int INzd = GetIntParameter(parameters, "INzd");
int INsm = GetIntParameter(parameters, "INsm");
int INrough = GetIntParameter(parameters, "INrough");
float ZZ0 = GetFloatParameter(parameters, "ZZ0");
int DB = GetIntParameter(parameters, "DB");
float[] UQ = GetFloatArrayParameter(parameters, "UQ");
float[] DQH = GetFloatArrayParameter(parameters, "DQH");
float dt = GetFloatParameter(parameters, "dt");
// 准备数组参数
float[] OUTT = new float[10000]; // 输出数组初始大小为10000
float[] OUTQ = new float[10000]; // 输出数组初始大小为10000
float[] OUTH = new float[10000]; // 输出数组初始大小为10000
// 调用Fortran函数
SUB_SHUILIANGJISUAN(
ref NDATA,
ref LONG,
ref INbd,
ref INzd,
ref INsm,
ref INrough,
ref ZZ0,
ref DB,
UQ,
DQH,
ref dt,
OUTT,
OUTQ,
OUTH
);
// 更新输出参数
UpdateArrayParameter(parameters, "OUTT", OUTT);
UpdateArrayParameter(parameters, "OUTQ", OUTQ);
UpdateArrayParameter(parameters, "OUTH", OUTH);
// 处理输出数组
// 处理输出数组 OUTT
// 注意:没有找到明确的长度参数,使用非零元素数量
{
int nonZeroCount = 0;
for (int i = 0; i < OUTT.Length; i++)
{
if (OUTT[i] != 0) nonZeroCount = i + 1;
}
if (nonZeroCount > 0)
{
float[] resultArray = new float[nonZeroCount];
Array.Copy(OUTT, resultArray, nonZeroCount);
UpdateArrayParameter(parameters, "OUTT", resultArray);
}
}
// 处理输出数组 OUTQ
// 注意:没有找到明确的长度参数,使用非零元素数量
{
int nonZeroCount = 0;
for (int i = 0; i < OUTQ.Length; i++)
{
if (OUTQ[i] != 0) nonZeroCount = i + 1;
}
if (nonZeroCount > 0)
{
float[] resultArray = new float[nonZeroCount];
Array.Copy(OUTQ, resultArray, nonZeroCount);
UpdateArrayParameter(parameters, "OUTQ", resultArray);
}
}
// 处理输出数组 OUTH
// 注意:没有找到明确的长度参数,使用非零元素数量
{
int nonZeroCount = 0;
for (int i = 0; i < OUTH.Length; i++)
{
if (OUTH[i] != 0) nonZeroCount = i + 1;
}
if (nonZeroCount > 0)
{
float[] resultArray = new float[nonZeroCount];
Array.Copy(OUTH, resultArray, nonZeroCount);
UpdateArrayParameter(parameters, "OUTH", resultArray);
}
}
// 返回结果 - 只返回原始请求的结构,但包含更新后的参数
var result = new FortranRequest
{
FuncName = request.FuncName,
ClassName = request.ClassName,
Parameters = parameters
};
return JsonSerializer.Serialize(result, options);
}
catch (Exception ex)
{
return JsonSerializer.Serialize(new { error = ex.Message });
}
}
private float GetFloatParameter(List<FortranParameter> parameters, string name)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param == null || param.Data == null)
{
return 0.0f;
}
if (param.Data is JsonElement element)
{
if (element.ValueKind == JsonValueKind.Number)
{
return element.GetSingle();
}
}
return Convert.ToSingle(param.Data);
}
private double GetDoubleParameter(List<FortranParameter> parameters, string name)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param == null || param.Data == null)
{
return 0.0;
}
if (param.Data is JsonElement element)
{
if (element.ValueKind == JsonValueKind.Number)
{
return element.GetSingle();
}
}
return Convert.ToDouble(param.Data);
}
private int GetIntParameter(List<FortranParameter> parameters, string name)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param == null || param.Data == null)
{
return 0;
}
if (param.Data is JsonElement element)
{
if (element.ValueKind == JsonValueKind.Number)
{
return element.GetInt32();
}
}
return Convert.ToInt32(param.Data);
}
private int[] GetIntArrayParameter(List<FortranParameter> parameters, string name)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param == null || param.Data == null)
{
return Array.Empty<int>();
}
if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
{
var array = new List<int>();
foreach (var item in element.EnumerateArray())
{
if (item.ValueKind == JsonValueKind.Number)
{
array.Add(item.GetInt32());
}
}
return array.ToArray();
}
if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
{
var array = new List<int>();
foreach (var item in enumerable)
{
array.Add(Convert.ToInt32(item));
}
return array.ToArray();
}
return Array.Empty<int>();
}
private float[] GetFloatArrayParameter(List<FortranParameter> parameters, string name)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param == null || param.Data == null)
{
return Array.Empty<float>();
}
if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
{
var array = new List<float>();
foreach (var item in element.EnumerateArray())
{
if (item.ValueKind == JsonValueKind.Number)
{
array.Add(item.GetSingle());
}
}
return array.ToArray();
}
if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
{
var array = new List<float>();
foreach (var item in enumerable)
{
array.Add(Convert.ToSingle(item));
}
return array.ToArray();
}
return Array.Empty<float>();
}
private double[] GetDoubleArrayParameter(List<FortranParameter> parameters, string name)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param == null || param.Data == null)
{
return Array.Empty<double>();
}
if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
{
var array = new List<double>();
foreach (var item in element.EnumerateArray())
{
if (item.ValueKind == JsonValueKind.Number)
{
array.Add(item.GetSingle());
}
}
return array.ToArray();
}
if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
{
var array = new List<double>();
foreach (var item in enumerable)
{
array.Add(Convert.ToSingle(item));
}
return array.ToArray();
}
return Array.Empty<double>();
}
private void UpdateParameter(List<FortranParameter> parameters, string name, object value)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param != null)
{
param.Data = value;
}
}
private void UpdateArrayParameter(List<FortranParameter> parameters, string name, float[] value)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param != null)
{
param.Data = value;
}
}
private void UpdateDoubleArrayParameter(List<FortranParameter> parameters, string name, double[] value)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param != null)
{
param.Data = value;
}
}
private void UpdateIntArrayParameter(List<FortranParameter> parameters, string name, int[] value)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param != null)
{
param.Data = value;
}
}
// 添加新的辅助方法来处理二维数组
private double[,] GetDouble2DArrayParameter(List<FortranParameter> parameters, string name, int rows, int cols)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param == null || param.Data == null)
{
return new double[rows, cols];
}
double[,] result = new double[rows, cols];
if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
{
int index = 0;
foreach (var item in element.EnumerateArray())
{
if (item.ValueKind == JsonValueKind.Number)
{
int row = index / cols;
int col = index % cols;
if (row < rows && col < cols)
{
result[row, col] = item.GetDouble();
}
index++;
}
}
}
else if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
{
int index = 0;
foreach (var item in enumerable)
{
int row = index / cols;
int col = index % cols;
if (row < rows && col < cols)
{
result[row, col] = Convert.ToDouble(item);
}
index++;
}
}
return result;
}
// 添加更新二维数组参数的方法
private void UpdateDouble2DArrayParameter(List<FortranParameter> parameters, string name, double[,] value)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param != null)
{
// 将二维数组转换为一维数组以便于JSON序列化
int rows = value.GetLength(0);
int cols = value.GetLength(1);
double[] flatArray = new double[rows * cols];
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
flatArray[i * cols + j] = value[i, j];
}
}
param.Data = flatArray;
}
}
// float版本
private float[,] GetFloat2DArrayParameter(List<FortranParameter> parameters, string name, int rows, int cols)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param == null || param.Data == null)
{
return new float[rows, cols];
}
float[,] result = new float[rows, cols];
if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
{
int index = 0;
foreach (var item in element.EnumerateArray())
{
if (item.ValueKind == JsonValueKind.Number)
{
int row = index / cols;
int col = index % cols;
if (row < rows && col < cols)
{
result[row, col] = item.GetSingle();
}
index++;
}
}
}
else if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
{
int index = 0;
foreach (var item in enumerable)
{
int row = index / cols;
int col = index % cols;
if (row < rows && col < cols)
{
result[row, col] = Convert.ToSingle(item);
}
index++;
}
}
return result;
}
private void UpdateFloat2DArrayParameter(List<FortranParameter> parameters, string name, float[,] value)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param != null)
{
int rows = value.GetLength(0);
int cols = value.GetLength(1);
float[] flatArray = new float[rows * cols];
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
flatArray[i * cols + j] = value[i, j];
}
}
param.Data = flatArray;
}
}
// int版本
private int[,] GetInt2DArrayParameter(List<FortranParameter> parameters, string name, int rows, int cols)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param == null || param.Data == null)
{
return new int[rows, cols];
}
int[,] result = new int[rows, cols];
if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array)
{
int index = 0;
foreach (var item in element.EnumerateArray())
{
if (item.ValueKind == JsonValueKind.Number)
{
int row = index / cols;
int col = index % cols;
if (row < rows && col < cols)
{
result[row, col] = item.GetInt32();
}
index++;
}
}
}
else if (param.Data is System.Collections.IEnumerable enumerable && !(param.Data is string))
{
int index = 0;
foreach (var item in enumerable)
{
int row = index / cols;
int col = index % cols;
if (row < rows && col < cols)
{
result[row, col] = Convert.ToInt32(item);
}
index++;
}
}
return result;
}
private void UpdateInt2DArrayParameter(List<FortranParameter> parameters, string name, int[,] value)
{
var param = parameters.FirstOrDefault(p => p.Name == name);
if (param != null)
{
int rows = value.GetLength(0);
int cols = value.GetLength(1);
int[] flatArray = new int[rows * cols];
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
flatArray[i * cols + j] = value[i, j];
}
}
param.Data = flatArray;
}
}
}
}

16
appsettings.json Normal file
View File

@ -0,0 +1,16 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:5000"
}
}
}
}