From a024039f0524547e5f2681ec378819bcc45065d9 Mon Sep 17 00:00:00 2001 From: tp <1799149022@qq.com> Date: Wed, 14 May 2025 17:42:10 +0800 Subject: [PATCH] 1 --- Controllers/FortranController.cs | 74 ++++ Dockerfile | 88 +++++ Fortran/1.json | 329 ++++++++++++++++ Fortran/1.xlsx | Bin 0 -> 36602 bytes Fortran/MAIN_UFRN.f90 | 350 +++++++++++++++++ Fortran/SUB_BOUND.f90 | 164 ++++++++ Fortran/SUB_INT_A.f90 | 42 ++ Fortran/SUB_QZ.f90 | 350 +++++++++++++++++ Fortran/SUB_SECT.f90 | 46 +++ Fortran/~$1.xlsx | 0 FortranWebApi.csproj | 27 ++ HttpRequests/fortranwebapi.http | 49 +++ Models/ApiResponse.cs | 31 ++ Models/FortranParameter.cs | 22 ++ Models/FortranRequest.cs | 13 + Models/FortranRequestWrapper.cs | 7 + Program.cs | 80 ++++ Properties/launchSettings.json | 14 + Services/FortranInteropService.cs | 627 ++++++++++++++++++++++++++++++ appsettings.json | 16 + 20 files changed, 2329 insertions(+) create mode 100644 Controllers/FortranController.cs create mode 100644 Dockerfile create mode 100644 Fortran/1.json create mode 100644 Fortran/1.xlsx create mode 100644 Fortran/MAIN_UFRN.f90 create mode 100644 Fortran/SUB_BOUND.f90 create mode 100644 Fortran/SUB_INT_A.f90 create mode 100644 Fortran/SUB_QZ.f90 create mode 100644 Fortran/SUB_SECT.f90 create mode 100644 Fortran/~$1.xlsx create mode 100644 FortranWebApi.csproj create mode 100644 HttpRequests/fortranwebapi.http create mode 100644 Models/ApiResponse.cs create mode 100644 Models/FortranParameter.cs create mode 100644 Models/FortranRequest.cs create mode 100644 Models/FortranRequestWrapper.cs create mode 100644 Program.cs create mode 100644 Properties/launchSettings.json create mode 100644 Services/FortranInteropService.cs create mode 100644 appsettings.json diff --git a/Controllers/FortranController.cs b/Controllers/FortranController.cs new file mode 100644 index 0000000..46a63e2 --- /dev/null +++ b/Controllers/FortranController.cs @@ -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 _logger; + + public FortranCalculateController(FortranInteropService fortranService, ILogger 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(result, options); + + // 返回新的格式 + var response = new ApiResponse + { + Message = "Success", + Success = true, + Data = new ApiResponseData + { + Value = resultObj + } + }; + + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex, "处理Fortran请求时发生错误"); + return StatusCode(500, new + { + message = ex.Message, + success = false, + data = (object)null + }); + } + } + } +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..31c27e9 --- /dev/null +++ b/Dockerfile @@ -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 libMAIN_UFRN.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/libMAIN_UFRN.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/libMAIN_UFRN.so + +# 设置 LD_LIBRARY_PATH +ENV LD_LIBRARY_PATH=/app + +# 设置端口和监听地址 +ENV ASPNETCORE_URLS=http://+:5000 +EXPOSE 5000 + +ENTRYPOINT ["dotnet", "FortranWebApi.dll"] \ No newline at end of file diff --git a/Fortran/1.json b/Fortran/1.json new file mode 100644 index 0000000..310757c --- /dev/null +++ b/Fortran/1.json @@ -0,0 +1,329 @@ +{ + "fortranSourceFile": "D:\\\u5DE5\u4F5C2025\\\u7B97\u6CD5\u5E73\u53F0\\dll\\\u591A\u6CB3\u9053\u4E00\u7EF4\u6C34\u52A8\u529B\u6A21\u578B\\MAIN_UFRN\\MAIN_UFRN\\MAIN_UFRN - \u526F\u672C.f90", + "fortranFunctionName": "MAIN_UFRN", + "projectName": "FortranWebApi", + "outputDirectory": "D:\\\u5DE5\u4F5C2025\\\u7B97\u6CD5\u5E73\u53F0\\\u8F93\u51FA\u5E93\\\u591A\u6CB3\u9053\u4E00\u7EF4\u6C34\u52A8\u529B\u6A21\u578B", + "cmbLanguage": "Fortran", + "parameters": [ + { + "name": "nriver", + "dataType": "Integer", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "nsect", + "dataType": "Integer", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "krc", + "dataType": "Integer", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "ndata", + "dataType": "Integer", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Ns", + "dataType": "Integer", + "arrayType": "OneDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Pc", + "dataType": "Integer", + "arrayType": "OneDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Nrc", + "dataType": "Integer", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Lc", + "dataType": "Integer", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Dric", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "XJ", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Asave", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "ds", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "bd", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "zd", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "sm", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "rough", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "ZZ0", + "dataType": "Float", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Zctr", + "dataType": "Float", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Qp", + "dataType": "Float", + "arrayType": "OneDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "UB1", + "dataType": "Integer", + "arrayType": "OneDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "UB2", + "dataType": "Integer", + "arrayType": "OneDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "DB1", + "dataType": "Integer", + "arrayType": "OneDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "DB2", + "dataType": "Integer", + "arrayType": "OneDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "UBV", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "NUB", + "dataType": "Integer", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "DBV", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Gate", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "NDB", + "dataType": "Integer", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Aphi", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "period", + "dataType": "Float", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "dt", + "dataType": "Float", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "sita", + "dataType": "Float", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "sorz", + "dataType": "Float", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "sorq", + "dataType": "Float", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "epsz", + "dataType": "Float", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "epsq", + "dataType": "Float", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Bsor1", + "dataType": "Float", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Bsor2", + "dataType": "Float", + "arrayType": "Scalar", + "direction": "Input", + "description": "", + "isSelected": true + }, + { + "name": "Z", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Output", + "description": "", + "isSelected": true + }, + { + "name": "Q", + "dataType": "Float", + "arrayType": "TwoDimensional", + "direction": "Output", + "description": "", + "isSelected": true + } + ] +} \ No newline at end of file diff --git a/Fortran/1.xlsx b/Fortran/1.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..59db24058e92ed09d4854f46e73b8d743474b076 GIT binary patch literal 36602 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 literal 0 HcmV?d00001 diff --git a/Fortran/MAIN_UFRN.f90 b/Fortran/MAIN_UFRN.f90 new file mode 100644 index 0000000..dfe6f34 --- /dev/null +++ b/Fortran/MAIN_UFRN.f90 @@ -0,0 +1,350 @@ +SUBROUTINE MAIN_UFRN(nriver ,& !ӵ + nsect ,& !ӵֵ + krc ,& !ӵֵ + ndata ,& !ʱ + Ns ,& !ӵ + Pc ,& !ӵ + Nrc ,& !ӵ΢κ + Lc ,& ! + Dric ,& ! + XJ ,& ! + Asave ,& !ˮ + ds ,& !ӵ + bd ,& !ζ׿ + zd ,& !ζ׸߳ + sm ,& !ζϵ + rough ,& !ζ + ZZ0 ,& !ʼˮλ + Zctr ,& !ӵĩ˵բпˮλ + Qp ,& ! + UB1 ,& !α߽11ˮλ߽磬2߽,3ˮλϵ + UB2 ,& !α߽21߽磬2ڱ߽磩 + DB1 ,& !α߽11ˮλ߽磬2߽,3ˮλϵ + DB2 ,& !α߽21߽磬2ڱ߽磩 + UBV ,& !߽洦ˮλ + NUB ,& !ڱ߽ӵĺӵš΢α + DBV ,& !߽洦ˮλբ³λ + Gate ,& !Gate(1,i) = բGate(2,i) = Gate(3,i) = ʽC1Gate(4,i) = ʽC2 ʽQ=C1*B*e*dZ^C2еC1C2 + NDB ,& !ڱ߽ӵĺӵš΢α + Aphi ,& !ڱ߽磨ܺĹϵ + period ,& !Сʱ + dt ,& !ʱ䲽 + sita ,& !ˮʽȨϵ + sorz ,& !ˮλɳ + sorq ,& !ɳ + epsz ,& !ˮλƾ + epsq ,& !ƾ + Bsor1 ,& !ˮբ߽еɳ + Bsor2 ,& !ܺڱ߽еɳ + Z ,& !ӵˮλ + Q )& !ӵ + BIND(C, NAME="MAIN_UFRN") + + !DEC$ ATTRIBUTES DLLEXPORT::MAIN_UFRN + + IMPLICIT NONE + + INTEGER::nriver ! ӵ + INTEGER::nsect ! ӵֵ + INTEGER::krc ! ӵֵ + INTEGER::ndata ! ʱʱ䲽ݵ + INTEGER::MRIVER + integer River,tstep + INTEGER::I + INTEGER::II + + ! ӵ̬ + INTEGER::Ns(nriver) ! ӵά: NRIVER + INTEGER::Pc(nriver) ! ӵά: NRIVER + INTEGER::Nrc(krc,nriver) ! ͣ1-ͨ2-3-ˮ⣩ά: KRC, NRIVER + INTEGER::Lc(krc,nriver) ! ӵ΢αţά: KRC, NRIVER + + !߽ + INTEGER::UB1(nriver) ! α߽ͣ1-ˮλ2-3-ˮλϵά: NRIVER + INTEGER::UB2(nriver) ! α߽λã1-߽磬2-ڱ߽磩ά: NRIVER + INTEGER::DB1(nriver) ! α߽ͣͬϣά: NRIVER + INTEGER::DB2(nriver) ! α߽λãͬϣά: NRIVER + INTEGER::NUB(2,nriver) ! ڱ߽ӵĺӵ΢Σά: 2, NRIVER + INTEGER::NDB(2,nriver) ! ڱ߽ӵĺӵ΢Σά: 2, NRIVER + + REAL::ZZ0 ! ʼˮλm + REAL::Zctr ! ӵĩ˵բˮλm + REAL::period ! ʱСʱ + REAL::dt ! ʱ䲽룩 + REAL::sita ! ʽȨϵ0.5~1.0 + REAL::sorz ! ˮλɳӣ0~1 + REAL::sorq ! ɳӣ0~1 + REAL::epsz ! ˮλȣm + REAL::epsq ! ȣm?/s + REAL::Bsor1 ! ˮբ߽ɳ + REAL::Bsor2 ! ߽ܺɳ + REAL::ql + REAL::condu,condd(3) + REAL::Bs,As,Rs,Cs + + !ӵ + REAL::Dric(krc,nriver) ! 1-1ά: KRC, NRIVER + REAL::Qj(ndata,krc,nriver) ! ̣ά: NDATA, KRC, NRIVER + REAL::Asave(krc,nriver) ! ˮm?ά: KRC, NRIVER + + !ӵ + REAL::ds(nsect - 1,nriver) ! ΢γȣmά: nsect - 1, NRIVER + REAL::bd(nsect,nriver) ! ׿mά: NSECT, NRIVER + REAL::zd(nsect,nriver) ! ׸̣߳mά: NSECT, NRIVER + REAL::sm(nsect,nriver) ! ϵ-ά: NSECT, NRIVER + REAL::rough(nsect,nriver) ! ʣϵά: NSECT, NRIVER + + !߽ + REAL::UBV(ndata,nriver) ! ߽ˮλ/̣ά: NDATA, NRIVER + REAL::DBV(ndata,nriver) ! ߽ˮλ/̣ά: NDATA, NRIVER + REAL::Gate(4,nriver) ! բŲC1C2ά: 4, NRIVER + REAL::Aphi(2,nriver) ! ܺϵά: 2, NRIVER + REAL::Qp(ndata) ! ̣m?/sά: NDATA + + REAL::Z0(nsect,nriver) + REAL::Q0(nsect,nriver) + REAL::Z(nsect,nriver) + REAL::Q(nsect,nriver) + REAL::V(nsect,nriver) + REAL::Zc(nsect,nriver),Qc(nsect,nriver) + REAL::XJ(ndata,nriver*krc) + + REAL::maxtstep + REAL::maxiter + REAL::ttime + REAL::fc + REAL::Scanal + REAL::dzmax + REAL::dqmax + REAL::dz + REAL::dq + REAL::maxz_r + REAL::maxz_s + REAL::maxq_r + REAL::maxq_s + + INTEGER::Is + INTEGER::iter + INTEGER::J,K + + MRIVER = nsect - 1 + + Scanal = 0.0 ! ScanalкӵĺϼƳȣڼԲql + + DO I = 1,ndata + DO J = 1,KRC + DO K = 1,NRIVER + Qj(i,j,k) = xj(i,j*k) + end do + end do + end do + + DO J = 1,nriver + DO I = 1,Ns(j)-1 + Scanal = Scanal + ds(i,j) ! ÿӵ΢εijȣͨ㡢΢μˮijȡ0ÿӵĩ΢γȱȡһֵ + END DO + END DO + + ! ʱ䲽 + 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 + + DO tstep=1,maxtstep ! ۼ + + ttime=dt*tstep/3600.0 ! ʵʱ + + 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 ) + + ! ĵʽδ֪ʱˮλZQ + 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 + 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 + exit + 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 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 + + END SUBROUTINE MAIN_UFRN \ No newline at end of file diff --git a/Fortran/SUB_BOUND.f90 b/Fortran/SUB_BOUND.f90 new file mode 100644 index 0000000..e59173f --- /dev/null +++ b/Fortran/SUB_BOUND.f90 @@ -0,0 +1,164 @@ +! ================================================================================== +! ȷʱ̺Ӷαֵ߽ӳ +! ================================================================================== +! +! ˵߽һ̣ndataݵ㣨1Сʱˮλ߽缴Ϊˮλ̣ +! ߽缴Ϊ̣ˮλϵֻ߽޵բբ³λ̡ +! ӵĩ˵բˮλϵ򻯳Q=C1*B*e*dZ^C2Z>ZtideZ>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) + INTEGER::DB2(nriver),NUB(2,nriver),NDB(2,nriver) + REAL::UBV(ndata,nriver),Aphi(2,nriver),DBV(ndata,nriver) + REAL::Gate(4,nriver),ql,Zctr + ! calcu + REAL::dt,sita,Bsor1,Bsor2 + ! zzqq + REAL::Z0(nsect,nriver),Q0(nsect,nriver),Z(nsect,nriver) + REAL::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 + + + + diff --git a/Fortran/SUB_INT_A.f90 b/Fortran/SUB_INT_A.f90 new file mode 100644 index 0000000..ea1bf35 --- /dev/null +++ b/Fortran/SUB_INT_A.f90 @@ -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 diff --git a/Fortran/SUB_QZ.f90 b/Fortran/SUB_QZ.f90 new file mode 100644 index 0000000..5822451 --- /dev/null +++ b/Fortran/SUB_QZ.f90 @@ -0,0 +1,350 @@ +! ================================================================================== +! ĵʽδ֪ʱˮλZQӳ +! ================================================================================== + 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) + REAL::sm(nsect,nriver), rough(nsect,nriver) + ! boundary + INTEGER::UB1(nriver),UB2(nriver),DB1(nriver),DB2(nriver) + INTEGER::NUB(2,nriver),NDB(2,nriver) + REAL::UBV(ndata,nriver),Aphi(2,nriver),DBV(ndata,nriver) + REAL::Gate(4,nriver),ql,Zctr + ! calcu + REAL::dt,sita,Bsor1,Bsor2 + ! zzqq + REAL::Z0(nsect,nriver),Q0(nsect,nriver),Z(nsect,nriver) + REAL::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㴦ˮBAˮ뾶RQˮλ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=2ˮLc=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 \ No newline at end of file diff --git a/Fortran/SUB_SECT.f90 b/Fortran/SUB_SECT.f90 new file mode 100644 index 0000000..e635530 --- /dev/null +++ b/Fortran/SUB_SECT.f90 @@ -0,0 +1,46 @@ +! ================================================================================== +! 漸Ҫصӳ +! ================================================================================== + 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) + REAL::sm(nsect,nriver), rough(nsect,nriver) + ! BARC + REAL::Bs,As,Rs,Cs + +! ---------------------------------------------------------------------------------- +! BsAsRsCsӦˮλZsĶҪأζ棩 +! ---------------------------------------------------------------------------------- + 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 + diff --git a/Fortran/~$1.xlsx b/Fortran/~$1.xlsx new file mode 100644 index 0000000..e69de29 diff --git a/FortranWebApi.csproj b/FortranWebApi.csproj new file mode 100644 index 0000000..6a4d551 --- /dev/null +++ b/FortranWebApi.csproj @@ -0,0 +1,27 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + + + PreserveNewest + + + + \ No newline at end of file diff --git a/HttpRequests/fortranwebapi.http b/HttpRequests/fortranwebapi.http new file mode 100644 index 0000000..b79b947 --- /dev/null +++ b/HttpRequests/fortranwebapi.http @@ -0,0 +1,49 @@ +@FortranWebApi_HostAddress = http://localhost:5000 + +### 测试Fortran请求 + +POST {{FortranWebApi_HostAddress}}/FortranCalculate +Content-Type: application/json + +{ + "text": "{\"FuncName\":\"calculate_main\",\"ClassName\":\"\",\"Par\":[ {"Name":"nriver","DataType":"0","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"nsect","DataType":"0","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"krc","DataType":"0","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"ndata","DataType":"0","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"Ns","DataType":"0","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"Pc","DataType":"0","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"Nrc","DataType":"0","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"Lc","DataType":"0","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"Dric","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"XJ","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"Asave","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"ds","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"bd","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"zd","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"sm","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"rough","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"ZZ0","DataType":"1","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"Zctr","DataType":"1","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"Qp","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"UB1","DataType":"0","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"UB2","DataType":"0","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"DB1","DataType":"0","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"DB2","DataType":"0","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"UBV","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"NUB","DataType":"0","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"DBV","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"Gate","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"NDB","DataType":"0","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"Aphi","DataType":"1","ArrayType":"1","IsOut":"2","Data":[]}, + {"Name":"period","DataType":"1","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"dt","DataType":"1","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"sita","DataType":"1","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"sorz","DataType":"1","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"sorq","DataType":"1","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"epsz","DataType":"1","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"epsq","DataType":"1","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"Bsor1","DataType":"1","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"Bsor2","DataType":"1","ArrayType":"0","IsOut":"2","Data":0}, + {"Name":"Z","DataType":"1","ArrayType":"1","IsOut":"1","Data":[]}, + {"Name":"Q","DataType":"1","ArrayType":"1","IsOut":"1","Data":[]}]}" +} \ No newline at end of file diff --git a/Models/ApiResponse.cs b/Models/ApiResponse.cs new file mode 100644 index 0000000..3845aa5 --- /dev/null +++ b/Models/ApiResponse.cs @@ -0,0 +1,31 @@ +using System.Text.Json.Serialization; + +namespace FortranWebApi.Models +{ + public class ApiResponse + { + [JsonPropertyName("message")] + public string Message { get; set; } = "Success"; + + [JsonPropertyName("success")] + public bool Success { get; set; } = true; + + [JsonPropertyName("data")] + public ApiResponseData? Data { get; set; } + } + + public class ApiResponseData + { + [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; } + } +} \ No newline at end of file diff --git a/Models/FortranParameter.cs b/Models/FortranParameter.cs new file mode 100644 index 0000000..78d68f0 --- /dev/null +++ b/Models/FortranParameter.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Models/FortranRequest.cs b/Models/FortranRequest.cs new file mode 100644 index 0000000..e17fc88 --- /dev/null +++ b/Models/FortranRequest.cs @@ -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 Parameters { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/Models/FortranRequestWrapper.cs b/Models/FortranRequestWrapper.cs new file mode 100644 index 0000000..49c36c2 --- /dev/null +++ b/Models/FortranRequestWrapper.cs @@ -0,0 +1,7 @@ +namespace FortranWebApi.Models +{ + public class FortranRequestWrapper + { + public string Text { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..ac334f1 --- /dev/null +++ b/Program.cs @@ -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(); + +// 配置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(); +} \ No newline at end of file diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 0000000..295de27 --- /dev/null +++ b/Properties/launchSettings.json @@ -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" + } + } +} \ No newline at end of file diff --git a/Services/FortranInteropService.cs b/Services/FortranInteropService.cs new file mode 100644 index 0000000..2b2ddcd --- /dev/null +++ b/Services/FortranInteropService.cs @@ -0,0 +1,627 @@ +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 == "libMAIN_UFRN.so") + { + // 尝试从当前目录加载 + string currentDir = Directory.GetCurrentDirectory(); + string libraryPath = Path.Combine(currentDir, "libMAIN_UFRN.so"); + + if (File.Exists(libraryPath)) + { + return NativeLibrary.Load(libraryPath); + } + } + + // 回退到默认加载行为 + return IntPtr.Zero; + }); + } + + // DllImport声明 + [DllImport("libMAIN_UFRN.so", CallingConvention = CallingConvention.Cdecl, EntryPoint = "MAIN_UFRN")] + private static extern void MAIN_UFRN( + ref int nriver, + ref int nsect, + ref int krc, + ref int ndata, + int[] Ns, + int[] Pc, + int[] Nrc, + int[] Lc, + float[] Dric, + float[] XJ, + float[] Asave, + float[] ds, + float[] bd, + float[] zd, + float[] sm, + float[] rough, + ref float ZZ0, + ref float Zctr, + float[] Qp, + int[] UB1, + int[] UB2, + int[] DB1, + int[] DB2, + float[] UBV, + int[] NUB, + float[] DBV, + float[] Gate, + int[] NDB, + float[] Aphi, + ref float period, + ref float dt, + ref float sita, + ref float sorz, + ref float sorq, + ref float epsz, + ref float epsq, + ref float Bsor1, + ref float Bsor2, + float[] Z, + float[] Q + ); + + public string ProcessFortranRequest(string requestText) + { + try + { + // 解析请求 + var options = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + WriteIndented = true + }; + + var request = JsonSerializer.Deserialize(requestText, options); + if (request == null) + { + throw new ArgumentException("无效的请求格式"); + } + + // 准备输入参数 + var parameters = request.Parameters; + + // 提取参数 + int nriver = GetIntParameter(parameters, "nriver"); + int nsect = GetIntParameter(parameters, "nsect"); + int krc = GetIntParameter(parameters, "krc"); + int ndata = GetIntParameter(parameters, "ndata"); + int[] Ns = GetIntArrayParameter(parameters, "Ns"); + int[] Pc = GetIntArrayParameter(parameters, "Pc"); + int[] Nrc = GetIntArrayParameter(parameters, "Nrc"); + int[] Lc = GetIntArrayParameter(parameters, "Lc"); + float[] Dric = GetFloatArrayParameter(parameters, "Dric"); + float[] XJ = GetFloatArrayParameter(parameters, "XJ"); + float[] Asave = GetFloatArrayParameter(parameters, "Asave"); + float[] ds = GetFloatArrayParameter(parameters, "ds"); + float[] bd = GetFloatArrayParameter(parameters, "bd"); + float[] zd = GetFloatArrayParameter(parameters, "zd"); + float[] sm = GetFloatArrayParameter(parameters, "sm"); + float[] rough = GetFloatArrayParameter(parameters, "rough"); + float ZZ0 = GetFloatParameter(parameters, "ZZ0"); + float Zctr = GetFloatParameter(parameters, "Zctr"); + float[] Qp = GetFloatArrayParameter(parameters, "Qp"); + int[] UB1 = GetIntArrayParameter(parameters, "UB1"); + int[] UB2 = GetIntArrayParameter(parameters, "UB2"); + int[] DB1 = GetIntArrayParameter(parameters, "DB1"); + int[] DB2 = GetIntArrayParameter(parameters, "DB2"); + float[] UBV = GetFloatArrayParameter(parameters, "UBV"); + int[] NUB = GetIntArrayParameter(parameters, "NUB"); + float[] DBV = GetFloatArrayParameter(parameters, "DBV"); + float[] Gate = GetFloatArrayParameter(parameters, "Gate"); + int[] NDB = GetIntArrayParameter(parameters, "NDB"); + float[] Aphi = GetFloatArrayParameter(parameters, "Aphi"); + float period = GetFloatParameter(parameters, "period"); + float dt = GetFloatParameter(parameters, "dt"); + float sita = GetFloatParameter(parameters, "sita"); + float sorz = GetFloatParameter(parameters, "sorz"); + float sorq = GetFloatParameter(parameters, "sorq"); + float epsz = GetFloatParameter(parameters, "epsz"); + float epsq = GetFloatParameter(parameters, "epsq"); + float Bsor1 = GetFloatParameter(parameters, "Bsor1"); + float Bsor2 = GetFloatParameter(parameters, "Bsor2"); + + + // 准备数组参数 + float[] Z = new float[10000]; // 输出数组,初始大小为10000 + float[] Q = new float[10000]; // 输出数组,初始大小为10000 + + + // 调用Fortran函数 + MAIN_UFRN( + ref nriver, + ref nsect, + ref krc, + ref ndata, + Ns, + Pc, + Nrc, + Lc, + Dric, + XJ, + Asave, + ds, + bd, + zd, + sm, + rough, + ref ZZ0, + ref Zctr, + Qp, + UB1, + UB2, + DB1, + DB2, + UBV, + NUB, + DBV, + Gate, + NDB, + Aphi, + ref period, + ref dt, + ref sita, + ref sorz, + ref sorq, + ref epsz, + ref epsq, + ref Bsor1, + ref Bsor2, + Z, + Q + ); + + // 更新输出参数 + UpdateParameter(parameters, "Z", Z); + UpdateParameter(parameters, "Q", Q); + + + // 处理输出数组 + // 处理输出数组 Z + // 注意:没有找到明确的长度参数,使用非零元素数量 + { + int nonZeroCount = 0; + for (int i = 0; i < Z.Length; i++) + { + if (Z[i] != 0) nonZeroCount = i + 1; + } + if (nonZeroCount > 0) + { + float[] resultArray = new float[nonZeroCount]; + Array.Copy(Z, resultArray, nonZeroCount); + UpdateArrayParameter(parameters, "Z", resultArray); + } + } + // 处理输出数组 Q + // 注意:没有找到明确的长度参数,使用非零元素数量 + { + int nonZeroCount = 0; + for (int i = 0; i < Q.Length; i++) + { + if (Q[i] != 0) nonZeroCount = i + 1; + } + if (nonZeroCount > 0) + { + float[] resultArray = new float[nonZeroCount]; + Array.Copy(Q, resultArray, nonZeroCount); + UpdateArrayParameter(parameters, "Q", 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 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 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 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 parameters, string name) + { + var param = parameters.FirstOrDefault(p => p.Name == name); + if (param == null || param.Data == null) + { + return Array.Empty(); + } + + if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array) + { + var array = new List(); + 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(); + foreach (var item in enumerable) + { + array.Add(Convert.ToInt32(item)); + } + return array.ToArray(); + } + + return Array.Empty(); + } + private float[] GetFloatArrayParameter(List parameters, string name) + { + var param = parameters.FirstOrDefault(p => p.Name == name); + if (param == null || param.Data == null) + { + return Array.Empty(); + } + + if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array) + { + var array = new List(); + 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(); + foreach (var item in enumerable) + { + array.Add(Convert.ToSingle(item)); + } + return array.ToArray(); + } + + return Array.Empty(); + } + private double[] GetDoubleArrayParameter(List parameters, string name) + { + var param = parameters.FirstOrDefault(p => p.Name == name); + if (param == null || param.Data == null) + { + return Array.Empty(); + } + + if (param.Data is JsonElement element && element.ValueKind == JsonValueKind.Array) + { + var array = new List(); + 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(); + foreach (var item in enumerable) + { + array.Add(Convert.ToSingle(item)); + } + return array.ToArray(); + } + + return Array.Empty(); + } + private void UpdateParameter(List parameters, string name, object value) + { + var param = parameters.FirstOrDefault(p => p.Name == name); + if (param != null) + { + param.Data = value; + } + } + + private void UpdateArrayParameter(List parameters, string name, float[] value) + { + var param = parameters.FirstOrDefault(p => p.Name == name); + if (param != null) + { + param.Data = value; + } + } + private void UpdateDoubleArrayParameter(List parameters, string name, double[] value) + { + var param = parameters.FirstOrDefault(p => p.Name == name); + if (param != null) + { + param.Data = value; + } + } + private void UpdateIntArrayParameter(List parameters, string name, int[] value) + { + var param = parameters.FirstOrDefault(p => p.Name == name); + if (param != null) + { + param.Data = value; + } + } + // 添加新的辅助方法来处理二维数组 + private double[,] GetDouble2DArrayParameter(List 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 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 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 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 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 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; + } + } + } +} \ No newline at end of file diff --git a/appsettings.json b/appsettings.json new file mode 100644 index 0000000..dcb0750 --- /dev/null +++ b/appsettings.json @@ -0,0 +1,16 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://0.0.0.0:5000" + } + } + } +} \ No newline at end of file